ฉันจะเพิ่มการสะท้อนกลับไปยังแอปพลิเคชัน C ++ ได้อย่างไร


263

ฉันต้องการที่จะสามารถตรวจสอบคลาส C ++ สำหรับชื่อเนื้อหา (เช่นสมาชิกและประเภทของพวกเขา) ฯลฯ ฉันกำลังพูดภาษา C ++ ที่นี่ไม่ใช่จัดการ C ++ ซึ่งมีการสะท้อนกลับ ฉันรู้ว่า C ++ ให้ข้อมูลที่ จำกัด โดยใช้ RTTI ห้องสมุดเพิ่มเติมใด (หรือเทคนิคอื่น ๆ ) สามารถให้ข้อมูลนี้ได้บ้าง?


18
โชคดีที่คุณไม่สามารถทำได้หากไม่มีมาโครและการประมวลผลล่วงหน้าอื่น ๆ เนื่องจากข้อมูลเมตาที่ต้องการไม่มีอยู่นอกเสียจากคุณจะสร้างด้วยตนเองผ่านเวทมนต์ประมวลผลมาโครบางตัว
jalf

6
ข้อมูลที่คุณสามารถรับกลับจาก RTTI นั้นไม่เพียงพอที่จะทำสิ่งต่าง ๆ ที่คุณต้องการสะท้อน คุณไม่สามารถทำซ้ำฟังก์ชันของสมาชิกในคลาสได้
Joseph Garvin

คำตอบ:


259

สิ่งที่คุณต้องทำคือให้ตัวประมวลผลล่วงหน้าสร้างข้อมูลการสะท้อนเกี่ยวกับฟิลด์ ข้อมูลนี้สามารถจัดเก็บเป็นคลาสที่ซ้อนกัน

ก่อนอื่นเพื่อให้ง่ายและสะอาดยิ่งขึ้นในการเขียนลงใน preprocessor เราจะใช้นิพจน์ที่พิมพ์ การแสดงออกที่พิมพ์เป็นเพียงการแสดงออกที่ทำให้ประเภทในวงเล็บ ดังนั้นแทนที่จะเขียนคุณจะเขียนint x (int) xต่อไปนี้เป็นมาโครที่มีประโยชน์บางอย่างเพื่อช่วยในการแสดงออกนิพจน์:

#define REM(...) __VA_ARGS__
#define EAT(...)

// Retrieve the type
#define TYPEOF(x) DETAIL_TYPEOF(DETAIL_TYPEOF_PROBE x,)
#define DETAIL_TYPEOF(...) DETAIL_TYPEOF_HEAD(__VA_ARGS__)
#define DETAIL_TYPEOF_HEAD(x, ...) REM x
#define DETAIL_TYPEOF_PROBE(...) (__VA_ARGS__),
// Strip off the type
#define STRIP(x) EAT x
// Show the type without parenthesis
#define PAIR(x) REM x

ต่อไปเราจะกำหนดREFLECTABLEแมโครเพื่อสร้างข้อมูลเกี่ยวกับแต่ละฟิลด์ (รวมถึงฟิลด์นั้น) มาโครนี้จะถูกเรียกเช่นนี้:

REFLECTABLE
(
    (const char *) name,
    (int) age
)

ดังนั้นการใช้Boost.PPเราวนซ้ำแต่ละอาร์กิวเมนต์และสร้างข้อมูลเช่นนี้:

// A helper metafunction for adding const to a type
template<class M, class T>
struct make_const
{
    typedef T type;
};

template<class M, class T>
struct make_const<const M, T>
{
    typedef typename boost::add_const<T>::type type;
};


#define REFLECTABLE(...) \
static const int fields_n = BOOST_PP_VARIADIC_SIZE(__VA_ARGS__); \
friend struct reflector; \
template<int N, class Self> \
struct field_data {}; \
BOOST_PP_SEQ_FOR_EACH_I(REFLECT_EACH, data, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))

#define REFLECT_EACH(r, data, i, x) \
PAIR(x); \
template<class Self> \
struct field_data<i, Self> \
{ \
    Self & self; \
    field_data(Self & self) : self(self) {} \
    \
    typename make_const<Self, TYPEOF(x)>::type & get() \
    { \
        return self.STRIP(x); \
    }\
    typename boost::add_const<TYPEOF(x)>::type & get() const \
    { \
        return self.STRIP(x); \
    }\
    const char * name() const \
    {\
        return BOOST_PP_STRINGIZE(STRIP(x)); \
    } \
}; \

สิ่งนี้จะสร้างค่าคงfields_nที่จำนวนของฟิลด์ที่สามารถสะท้อนได้ในชั้นเรียน จากนั้นจะเชี่ยวชาญในfield_dataแต่ละฟิลด์ นอกจากนี้ยังเป็นเพื่อนกับreflectorชั้นเรียนด้วยดังนั้นจึงสามารถเข้าถึงเขตข้อมูลได้แม้ว่าพวกเขาจะเป็นส่วนตัว:

struct reflector
{
    //Get field_data at index N
    template<int N, class T>
    static typename T::template field_data<N, T> get_field_data(T& x)
    {
        return typename T::template field_data<N, T>(x);
    }

    // Get the number of fields
    template<class T>
    struct fields
    {
        static const int n = T::fields_n;
    };
};

ทีนี้เพื่อย้ำเหนือช่องที่เราใช้รูปแบบผู้เยี่ยมชม เราสร้างช่วง MPL ตั้งแต่ 0 ถึงจำนวนของฟิลด์และเข้าถึงข้อมูลฟิลด์ที่ดัชนีนั้น จากนั้นจะส่งข้อมูลฟิลด์ไปยังผู้เข้าชมที่ผู้ใช้ระบุ:

struct field_visitor
{
    template<class C, class Visitor, class I>
    void operator()(C& c, Visitor v, I)
    {
        v(reflector::get_field_data<I::value>(c));
    }
};


template<class C, class Visitor>
void visit_each(C & c, Visitor v)
{
    typedef boost::mpl::range_c<int,0,reflector::fields<C>::n> range;
    boost::mpl::for_each<range>(boost::bind<void>(field_visitor(), boost::ref(c), v, _1));
}

ตอนนี้สำหรับช่วงเวลาแห่งความจริงเรารวมทั้งหมดเข้าด้วยกัน นี่คือวิธีที่เราสามารถกำหนดPersonคลาสที่สามารถสะท้อนได้:

struct Person
{
    Person(const char *name, int age)
        :
        name(name),
        age(age)
    {
    }
private:
    REFLECTABLE
    (
        (const char *) name,
        (int) age
    )
};

นี่คือprint_fieldsฟังก์ชั่นทั่วไปที่ใช้ข้อมูลการสะท้อนเพื่อวนซ้ำในฟิลด์:

struct print_visitor
{
    template<class FieldData>
    void operator()(FieldData f)
    {
        std::cout << f.name() << "=" << f.get() << std::endl;
    }
};

template<class T>
void print_fields(T & x)
{
    visit_each(x, print_visitor());
}

ตัวอย่างของการใช้print_fieldsกับPersonคลาสที่สามารถสะท้อนได้:

int main()
{
    Person p("Tom", 82);
    print_fields(p);
    return 0;
}

ผลลัพธ์ใด:

name=Tom
age=82

และ voila เราเพิ่งนำการสะท้อนกลับมาใช้ในภาษาซีพลัสพลัสในโค้ดต่ำกว่า 100 บรรทัด


106
ความรุ่งโรจน์สำหรับการแสดงวิธีใช้การไตร่ตรองแทนที่จะบอกว่าทำไม่ได้ มันเป็นคำตอบแบบนี้ที่ทำให้ SO เป็นทรัพยากรที่ยอดเยี่ยม
fearless_fool

4
โปรดทราบว่าหากคุณพยายามรวบรวมสิ่งนี้ภายใต้ Visual Studio คุณจะได้รับข้อผิดพลาดเนื่องจาก VS ไม่สามารถจัดการกับการขยายตัวของแมโครที่หลากหลายได้อย่างเหมาะสม สำหรับ VS ลองเพิ่ม: #define DETAIL_TYPEOF_INT2(tuple) DETAIL_TYPEOF_HEAD tupleและ #define DETAIL_TYPEOF_INT(...) DETAIL_TYPEOF_INT2((__VA_ARGS__)) เปลี่ยนคำจำกัดความของ TYPEOF (x) เป็น:#define TYPEOF(x) DETAIL_TYPEOF_INT(DETAIL_TYPEOF_PROBE x,)
Phenglei Kai

ฉันได้รับข้อผิดพลาด 'BOOST_PP_IIF_0' ไม่ได้ตั้งชื่อประเภท คุณช่วยได้ไหม
Ankit Zalani

3
ดูคำตอบของฉันเอง - stackoverflow.com/a/28399807/2338477ฉันได้แยกและบรรจุใหม่กำหนดทั้งหมดและไม่จำเป็นต้องเพิ่มห้องสมุด ในฐานะที่เป็นรหัสสาธิตฉันกำลังให้อนุกรมเพื่อ xml และเรียกคืนจาก xml
TarmoPikaro

106

มีสองชนิดของการreflectionว่ายน้ำ

  1. ตรวจสอบโดยทำซ้ำมากกว่าสมาชิกของประเภทระบุวิธีการและอื่น ๆ

    สิ่งนี้ไม่สามารถทำได้ด้วย C ++
  2. การตรวจสอบโดยการตรวจสอบว่าประเภทคลาส (คลาส, โครงสร้าง, สหภาพ) มีวิธีการหรือชนิดซ้อนกันที่ได้รับมาจากประเภทอื่นโดยเฉพาะ

    ชนิดของสิ่งนี้เป็นไปได้ด้วย C ++ template-tricksใช้ ใช้boost::type_traitsสำหรับหลายสิ่ง (เช่นการตรวจสอบว่าประเภทเป็นส่วนประกอบ) สำหรับการตรวจสอบการมีอยู่ของฟังก์ชันสมาชิกให้ใช้มันเป็นไปได้ไหมที่จะเขียนเทมเพลตเพื่อตรวจสอบการมีอยู่ของฟังก์ชัน? . สำหรับการตรวจสอบไม่ว่าจะเป็นประเภทที่ซ้อนกันบางอย่างที่มีอยู่ใช้ธรรมดาSFINAE

หากคุณกำลังมองหาวิธีที่จะทำให้สำเร็จ 1) เช่นมองว่ามีวิธีการเรียนกี่คลาสหรือชอบรับการแสดงสตริงของ id คลาสแล้วฉันกลัวว่าจะไม่มีวิธีมาตรฐาน C ++ ในการทำเช่นนี้ คุณต้องใช้อย่างใดอย่างหนึ่ง

  • Meta Compiler เช่น Qt Meta Object Compiler ซึ่งแปลรหัสของคุณโดยเพิ่มข้อมูลเมตาเพิ่มเติม
  • การจัดวางเฟรมของแมโครที่อนุญาตให้คุณเพิ่มข้อมูลเมตาที่ต้องการ คุณต้องบอกวิธีการทั้งหมดกรอบชื่อคลาสคลาสพื้นฐานและทุกอย่างที่ต้องการ

C ++ สร้างขึ้นโดยคำนึงถึงความเร็ว หากคุณต้องการการตรวจสอบระดับสูงเช่น C # หรือ Java มีฉันกลัวว่าฉันต้องบอกคุณว่าไม่มีทางที่ไม่มีความพยายาม


122
C ++ สร้างขึ้นโดยคำนึงถึงความเร็ว แต่ปรัชญาไม่ได้ "เร็วที่สุด" แทน "คุณไม่ต้องจ่ายเงินหากคุณไม่ได้ใช้" ฉันเชื่อว่าเป็นไปได้สำหรับภาษาที่จะใช้วิปัสสนาในวิธีที่เหมาะกับปรัชญานั้น C ++ เพียงแค่ขาดมัน
โจเซฟ Garvin

8
@Joseph: ควรทำอย่างไร? ต้องการให้จัดเก็บข้อมูลเมตาทั้งหมด ซึ่งหมายความว่าคุณต้องจ่ายเงินถึงแม้ว่าคุณจะไม่ได้ใช้ก็ตาม (ยกเว้นกรณีที่คุณสามารถทำเครื่องหมายแต่ละประเภทเป็น "สนับสนุนการสะท้อน" แต่เราก็เกือบจะลงที่เราอาจรวมทั้งการใช้กลอุบายแมโครที่มีอยู่.
jalf

25
@jalf: เฉพาะข้อมูลเมตาที่อาจจำเป็น หากเราพิจารณาการสะท้อนเวลารวบรวมเท่านั้นนี่เป็นเรื่องเล็กน้อย เช่นฟังก์ชั่นรวบรวมเวลาmembers<T>ซึ่งส่งคืนรายการสมาชิกทั้งหมดของ T หากเราต้องการให้มีการสะท้อนกลับรันไทม์ (เช่น RTTI ผสมกับการสะท้อนกลับ) คอมไพเลอร์จะยังรู้ประเภทฐานสะท้อนทั้งหมด มีโอกาสมากที่members<T>(T&)จะไม่มีการสร้างอินสแตนซ์ให้กับ T = std :: string ดังนั้น RTTI สำหรับ std :: string หรือคลาสที่ได้รับมาจึงไม่จำเป็นต้องรวม
MSalters

9
ห้องสมุดสะท้อน (กล่าวถึงด้านล่าง) เพิ่มการสะท้อนไปที่ C ++ โดยไม่ทำให้โค้ดที่มีอยู่ช้าลงที่: root.cern.ch/drupal/content/reflex
Joseph Lisee

6
@Joe: Reflection ไม่เคยทำให้รหัสที่มีอยู่ช้าลง มันทำให้สิ่งที่ส่งมอบมีขนาดใหญ่ขึ้น (เนื่องจากคุณต้องส่งฐานข้อมูลประเภทข้อมูล ... )
mmmmmmmm

56

และฉันจะรักม้า แต่ม้าก็ไม่ฟรี :-P

http://en.wikibooks.org/wiki/C%2B%2B_Programming/RTTIคือสิ่งที่คุณจะได้รับ ภาพสะท้อนที่คุณกำลังคิดถึง - ข้อมูลเมตาที่อธิบายอย่างเต็มรูปแบบที่รันไทม์ - ไม่มีอยู่สำหรับ C ++ โดยค่าเริ่มต้น


1
ฉันสองแบรด เทมเพลต C ++ นั้นค่อนข้างมีประสิทธิภาพและมีประสบการณ์มากมายในการทำงานประเภท 'การสะท้อน' ที่หลากหลายเช่นการเพิ่มไลบรารี่ 'ใด ๆ ' ลักษณะประเภท C ++ RTTI ฯลฯ ที่สามารถแก้ปัญหาการสะท้อนจำนวนมากได้ ดังนั้น Nick เป้าหมายของคุณคืออะไร
แอรอน

7
โหวตขึ้นโหวตจากโพนี่! ฉันโหวตขึ้นสองครั้งเพราะคำตอบของคุณก็สมควรเช่นกัน แต่น่าเศร้าที่ฉันได้รับเพียงครั้งเดียวดังนั้นม้าจึงชนะ :-)
Franci Penov

6
ฉันไม่เข้าใจว่าทำไมนี่ถึงเป็นการตอบสนองที่ฉลาด ฉันได้กล่าวไปแล้วว่าฉันต้องการอ้างอิงถึงห้องสมุดและอื่น ๆ เพื่อใช้งาน สะท้อน / วิปัสสนาสำหรับระบบต่าง ๆ เพื่อให้สามารถเข้าถึงสคริปต์อนุกรม ฯลฯ
นิค

3
@Nick: เขาตอบแล้ว ไม่สามารถทำได้ข้อมูลไม่มีอยู่และดังนั้นจึงไม่มีไลบรารีที่สามารถใช้งานได้สำหรับคุณ
jalf

@jalf ยังแปลกสำหรับฉันอ่านคนในโลกการเขียนโปรแกรมบอกว่าคิดว่า 'มันเป็นไปไม่ได้' และ 'ฉันไม่รู้ว่า' แน่ใจว่าเมทาดาทาไม่มีอยู่ แต่สามารถแทรกด้วยมาโครได้
Freddx L.

38

ข้อมูลมีอยู่ - แต่ไม่ใช่ในรูปแบบที่คุณต้องการและเฉพาะเมื่อคุณส่งออกชั้นเรียนของคุณ สิ่งนี้ใช้ได้ใน Windows ฉันไม่รู้เกี่ยวกับแพลตฟอร์มอื่น การใช้ตัวระบุคลาสหน่วยเก็บข้อมูลเป็นตัวอย่างเช่น:

class __declspec(export) MyClass
{
public:
    void Foo(float x);
}

สิ่งนี้ทำให้คอมไพเลอร์สร้างข้อมูลคำจำกัดความของคลาสลงใน DLL / exe แต่มันไม่ได้อยู่ในรูปแบบที่คุณสามารถใช้สำหรับการไตร่ตรอง

ที่ บริษัท ของฉันเราได้สร้างห้องสมุดที่ตีความข้อมูลเมตานี้และอนุญาตให้คุณสะท้อนคลาสโดยไม่ต้องใส่มาโครพิเศษอื่น ๆ ลงในชั้นเรียนด้วยตนเอง จะช่วยให้ฟังก์ชั่นที่จะเรียกว่าเป็นดังนี้:

MyClass *instance_ptr=new MyClass;
GetClass("MyClass")->GetFunction("Foo")->Invoke(instance_ptr,1.331);

สิ่งนี้ทำได้อย่างมีประสิทธิภาพ:

instance_ptr->Foo(1.331);

ฟังก์ชัน Invoke (this_pointer, ... ) มีอาร์กิวเมนต์ที่เป็นตัวแปร เห็นได้ชัดจากการเรียกฟังก์ชั่นด้วยวิธีนี้คุณจะหลีกเลี่ยงสิ่งต่าง ๆ เช่นความปลอดภัยแบบ const และอื่น ๆ ดังนั้นสิ่งเหล่านี้จึงถูกนำไปใช้ในการตรวจสอบรันไทม์

ฉันแน่ใจว่าสามารถปรับปรุงไวยากรณ์ได้และสามารถใช้งานได้กับ Win32 และ Win64 เท่านั้น เราพบว่ามีประโยชน์จริง ๆ สำหรับการมีอินเทอร์เฟซ GUI อัตโนมัติไปยังคลาสการสร้างคุณสมบัติใน C ++ การสตรีมไปยังและจาก XML เป็นต้นและไม่จำเป็นต้องได้รับมาจากคลาสพื้นฐานที่เฉพาะเจาะจง หากมีความต้องการมากพอเราอาจจะทำให้มันเป็นรูปเป็นร่างเพื่อปลดปล่อย


1
ฉันคิดว่าคุณหมายถึง__declspec(dllexport)และคุณสามารถดึงข้อมูลจากไฟล์. map หากคุณเปิดใช้งานการสร้างในระหว่างการสร้าง
Orwellophile

18

การสะท้อนกลับไม่ได้รับการสนับสนุนโดย C ++ นอกกรอบ มันน่าเศร้าเพราะมันทำให้การทดสอบการรับความเจ็บปวด

มีหลายวิธีในการทำภาพสะท้อน:

  1. ใช้ข้อมูลการดีบัก (ไม่ใช่แบบพกพา)
  2. โรยโค้ดของคุณด้วยมาโคร / เทมเพลตหรือวิธีการแหล่งอื่น ๆ (ดูน่าเกลียด)
  3. แก้ไขคอมไพเลอร์เช่น clang / gcc เพื่อสร้างฐานข้อมูล
  4. ใช้วิธีการของ Qt moc
  5. เพิ่มการสะท้อนกลับ
  6. การสะท้อนที่แม่นยำและแบน

ลิงก์แรกดูมีแนวโน้มมากที่สุด (ใช้ mod's to clang) ส่วนที่สองกล่าวถึงเทคนิคจำนวนหนึ่งส่วนที่สามคือแนวทางที่แตกต่างกันโดยใช้ gcc:

  1. http://www.donw.org/rfl/

  2. https://bitbucket.org/dwilliamson/clreflect

  3. https://root.cern.ch/how/how-use-reflex

ขณะนี้มีคณะทำงานสำหรับการสะท้อน C ++ ดูข่าวสำหรับ C ++ 14 @ CERN:

แก้ไข 13/08/17:

ตั้งแต่โพสต์ต้นฉบับมีจำนวนของความก้าวหน้าที่มีศักยภาพในการสะท้อน ต่อไปนี้ให้รายละเอียดเพิ่มเติมและการอภิปรายเกี่ยวกับเทคนิคและสถานะต่าง ๆ :

  1. ภาพสะท้อนคงที่ในกะลา
  2. ภาพสะท้อนคงที่
  3. การออกแบบสำหรับการสะท้อนคงที่

อย่างไรก็ตามมันไม่ได้ดูมีแนวโน้มในแนวทางการสะท้อนแสงแบบมาตรฐานใน C ++ ในอนาคตอันใกล้นี้เว้นแต่จะได้รับความสนใจจากชุมชนมากขึ้นในการสนับสนุนการสะท้อนใน C ++

รายละเอียดต่อไปนี้สถานะปัจจุบันตามข้อเสนอแนะจากการประชุมมาตรฐาน C ++ ครั้งล่าสุด:

แก้ไข 13/12/2560

Reflection ดูเหมือนจะเคลื่อนไปทาง C ++ 20 หรือมากกว่านั้นอาจเป็น TSR อย่างไรก็ตามการเคลื่อนไหวช้า

แก้ไข 15/09/2018

ร่าง TS ได้ถูกส่งไปยังหน่วยงานระดับชาติเพื่อลงคะแนนเสียง

ข้อความสามารถพบได้ที่นี่: https://github.com/cplusplus/reflection-ts

แก้ไข 11/07/2019

การสะท้อน TS นั้นสมบูรณ์แล้วและมีการออกความเห็นและออกเสียงลงคะแนนในช่วงฤดูร้อน (2019)

วิธีการวางโปรแกรมเมตาแม่แบบจะถูกแทนที่ด้วยวิธีการรวบรวมเวลาที่เรียบง่าย (ไม่สะท้อนใน TS)

แก้ไข 10/02/2020

มีคำขอให้สนับสนุนการสะท้อน TS ใน Visual Studio ที่นี่:

พูดคุยเกี่ยวกับ TS โดยผู้แต่ง David Sankel:

แก้ไข 17 มีนาคม 2563

ความคืบหน้าในการสะท้อนกำลังทำ สามารถดูรายงานจาก '2020-02 Prague ISO C ++ Committee Trip' ได้ที่นี่:

รายละเอียดเกี่ยวกับสิ่งที่กำลังพิจารณาสำหรับ C ++ 23 สามารถดูได้ที่นี่ (รวมถึงส่วนสั้น ๆ เกี่ยวกับ Reflection):

แก้ไขวันที่ 4 มิถุนายน 2020

กรอบใหม่ได้รับการเผยแพร่โดยเจฟฟ์ Preshing เรียกว่า 'ไม้อัด' ที่มีกลไกในการสะท้อนภาพรันไทม์ รายละเอียดเพิ่มเติมสามารถดูได้ที่นี่:

เครื่องมือและวิธีการใช้งานดูจะเป็นงานที่ขัดและใช้ง่ายที่สุด


1
ลิงก์เซิร์นเสีย
Mostowski Collapse

ลิงก์ cern ควรได้รับการแก้ไขแล้ว พวกเขามีแนวโน้มที่จะทำลายค่อนข้างบ่อยซึ่งเป็นความเจ็บปวด
Damian Dixon

คำตอบนี้เกี่ยวข้องกับการสะท้อนเวลารวบรวมเท่านั้นหรือไม่
einpoklum

@einpoklum โซลูชันปัจจุบันสำหรับการสะท้อนเท่านั้นคือเวลารวบรวมโดยปกติจะมีรหัสเมตา - เทมเพลตหรือมาโคร TS แบบร่างล่าสุดดูเหมือนว่าควรทำงานกับรันไทม์ แต่คุณจะต้องสร้างไลบรารีทั้งหมดด้วยคอมไพเลอร์ที่ถูกต้องสำหรับเมตาดาต้าที่จำเป็นเพื่อจัดเก็บ
Damian Dixon

@DamianDixon: นั่นไม่เป็นความจริง มีไลบรารีรีเฟลทจำนวนมาก ตอนนี้ได้รับพวกเขาค่อนข้าง clunky และทั้งการเลือกหรือต้องการ nodifications คอมไพเลอร์ แต่พวกเขายังคงอยู่ หากตามที่ฉันเข้าใจความคิดเห็นของคุณคุณอ้างถึงการรวบรวมเวลาเท่านั้นโปรดแก้ไขคำตอบของคุณเพื่อให้ชัดเจนยิ่งขึ้น
einpoklum

15

คุณต้องดูว่าคุณพยายามทำอะไรและ RTTI จะตอบสนองความต้องการของคุณหรือไม่ ฉันใช้การสะท้อนกลับหลอกของฉันเองเพื่อจุดประสงค์เฉพาะอย่างมาก ตัวอย่างเช่นฉันเคยต้องการที่จะสามารถกำหนดค่าความยืดหยุ่นในสิ่งที่การจำลองจะออก มันต้องการเพิ่มรหัสสำเร็จรูปบางอย่างให้กับคลาสที่จะส่งออก:

namespace {
  static bool b2 = Filter::Filterable<const MyObj>::Register("MyObject");
} 

bool MyObj::BuildMap()
{
  Filterable<const OutputDisease>::AddAccess("time", &MyObj::time);
  Filterable<const OutputDisease>::AddAccess("person", &MyObj::id);
  return true;
}

การเรียกครั้งแรกจะเพิ่มออบเจ็กต์นี้ลงในระบบการกรองซึ่งเรียกใช้BuildMap()เมธอดเพื่อหาวิธีการที่ใช้ได้

จากนั้นในไฟล์กำหนดค่าคุณสามารถทำสิ่งนี้:

FILTER-OUTPUT-OBJECT   MyObject
FILTER-OUTPUT-FILENAME file.txt
FILTER-CLAUSE-1        person == 1773
FILTER-CLAUSE-2        time > 2000

ด้วยการใช้เทมเพลตที่เกี่ยวข้องboostสิ่งนี้จะถูกแปลเป็นชุดของการเรียกใช้เมธอด ณ รันไทม์ (เมื่ออ่านไฟล์กำหนดค่า) ดังนั้นจึงมีประสิทธิภาพพอสมควร ฉันจะไม่แนะนำให้ทำเช่นนี้เว้นแต่คุณจะต้องการ แต่เมื่อคุณทำคุณสามารถทำสิ่งที่เจ๋งจริงๆ


ต้องรักฟังก์ชั่นเหล่านี้ที่จะกลับมาจริงเสมอ) ฉันคิดว่านี่เป็นภูมิคุ้มกันจากปัญหาการสั่ง init แบบคงที่?
paulm

14

ฉันจะแนะนำให้ใช้Qt

มีใบอนุญาตโอเพนซอร์สเช่นเดียวกับใบอนุญาตการค้า


1
ฉันดูที่นี่ แต่ใช้มาโครและรหัสแหล่งที่มาต้องแยกวิเคราะห์เพื่อสร้างรหัสเมตาดาต้า ฉันต้องการหลีกเลี่ยงขั้นตอนพิเศษนี้ ฉันต้องการใช้ไลบรารี C ++ หรือมาโครแบบง่าย ขอบคุณสำหรับความคิดที่ว่า
นิค

10
QT หรือห้องสมุดอื่นการใช้วิธีการที่คล้ายกันคือดีที่สุดที่คุณกำลังจะได้รับ
jalf

5
จ่ายในเวลารวบรวมหรือจ่าย ณ รันไทม์ - ไม่ว่าคุณจะจ่ายด้วยวิธีใด!
Martin Beckett

13

คุณพยายามทำอะไรกับเงาสะท้อน?
คุณสามารถใช้ Boost ลักษณะชนิดและtypeofห้องสมุดเป็นรูปแบบของการสะท้อน จำกัด เวลารวบรวม นั่นคือคุณสามารถตรวจสอบและแก้ไขคุณสมบัติพื้นฐานของประเภทที่ส่งไปยังเทมเพลต


13

แก้ไข : CAMPไม่ได้รับการบำรุงรักษาอีกแล้ว มีส้อมสองอัน:

  • หนึ่งเรียกอีกอย่างหนึ่งว่าCAMPเช่นกันและขึ้นอยู่กับ API เดียวกัน
  • ไตร่ตรองเป็นการเขียนซ้ำบางส่วนและจะเป็นที่ต้องการเนื่องจากไม่ต้องใช้ Boost มันใช้ C ++ 11

CAMPเป็นห้องสมุดลิขสิทธิ์ MIT (ชื่อเดิม LGPL) ที่เพิ่มการสะท้อนกลับไปยังภาษา C ++ ไม่จำเป็นต้องมีขั้นตอนการประมวลผลล่วงหน้าที่เฉพาะเจาะจงในการรวบรวม แต่จะต้องดำเนินการด้วยตนเอง

ปัจจุบัน Tegesoft ห้องสมุดใช้ Boost แต่ยังมีทางแยกโดยใช้ C ++ 11 ที่ไม่ต้องเพิ่ม


11

ฉันทำสิ่งที่คุณเป็นอยู่หลังจากนั้นและในขณะที่เป็นไปได้ที่จะได้รับการสะท้อนในระดับหนึ่งและเข้าถึงคุณลักษณะระดับสูงการปวดหัวบำรุงรักษาอาจไม่คุ้มค่า ระบบของฉันถูกใช้เพื่อแยกคลาส UI ออกจากตรรกะทางธุรกิจอย่างสมบูรณ์ผ่านการมอบหมายคล้ายกับแนวคิดของ Objective-C ในการส่งและส่งต่อข้อความ วิธีที่จะทำคือการสร้างคลาสฐานบางอย่างที่มีความสามารถในการทำแผนที่สัญลักษณ์ (ฉันใช้สระว่ายน้ำสตริง แต่คุณสามารถทำกับ enums ถ้าคุณชอบความเร็วและข้อผิดพลาดเวลาคอมไพล์จัดการกับความยืดหยุ่นรวม) เพื่อชี้ พอยน์เตอร์ฟังก์ชั่นบริสุทธิ์ แต่สิ่งที่คล้ายกับสิ่งที่ Boost มีกับ Boost ฟังก์ชั่น - ซึ่งฉันไม่สามารถเข้าถึงได้ในเวลา) คุณสามารถทำสิ่งเดียวกันกับตัวแปรสมาชิกของคุณได้ตราบใดที่คุณมีคลาสพื้นฐานทั่วไปที่สามารถแสดงค่าใด ๆ ได้ ระบบทั้งหมดเป็นระบบการเข้ารหัสและการมอบหมาย Key-Value ที่ไม่สะทกสะท้านโดยมีผลข้างเคียงเล็กน้อยที่อาจคุ้มค่ากับเวลาที่จำเป็นในการทำให้ทุกคลาสที่ใช้ระบบตรงกับวิธีการทั้งหมดและสมาชิกด้วยการโทรตามกฎหมาย : 1) คลาสใด ๆ สามารถเรียกใช้เมธอดใด ๆ บนคลาสอื่นโดยไม่ต้องรวมส่วนหัวหรือเขียนคลาสฐานปลอมเพื่อให้อินเตอร์เฟสสามารถกำหนดไว้ล่วงหน้าสำหรับคอมไพเลอร์; และ 2) ตัวรับและตัวตั้งค่าของตัวแปรสมาชิกนั้นง่ายต่อการทำให้เธรดปลอดภัยเนื่องจากการเปลี่ยนแปลงหรือการเข้าถึงค่าของพวกเขามักกระทำผ่าน 2 วิธีในคลาสพื้นฐานของวัตถุทั้งหมด ระบบทั้งหมดเป็นระบบการเข้ารหัสและการมอบหมาย Key-Value ที่ไม่สะทกสะท้านโดยมีผลข้างเคียงเล็กน้อยที่อาจคุ้มค่ากับเวลาที่จำเป็นในการทำให้ทุกคลาสที่ใช้ระบบตรงกับวิธีการทั้งหมดและสมาชิกด้วยการโทรตามกฎหมาย : 1) คลาสใด ๆ สามารถเรียกใช้เมธอดใด ๆ บนคลาสอื่นโดยไม่ต้องรวมส่วนหัวหรือเขียนคลาสฐานปลอมเพื่อให้อินเตอร์เฟสสามารถกำหนดไว้ล่วงหน้าสำหรับคอมไพเลอร์; และ 2) ตัวรับและตัวตั้งค่าของตัวแปรสมาชิกนั้นง่ายต่อการทำให้เธรดปลอดภัยเนื่องจากการเปลี่ยนแปลงหรือการเข้าถึงค่าของพวกเขามักกระทำผ่าน 2 วิธีในคลาสพื้นฐานของวัตถุทั้งหมด ระบบทั้งหมดเป็นระบบการเข้ารหัสและการมอบหมาย Key-Value ที่ไม่สะทกสะท้านโดยมีผลข้างเคียงเล็กน้อยที่อาจคุ้มค่ากับเวลาที่จำเป็นในการทำให้ทุกคลาสที่ใช้ระบบตรงกับวิธีการทั้งหมดและสมาชิกด้วยการโทรตามกฎหมาย : 1) คลาสใด ๆ สามารถเรียกใช้เมธอดใด ๆ บนคลาสอื่นโดยไม่ต้องรวมส่วนหัวหรือเขียนคลาสฐานปลอมเพื่อให้อินเตอร์เฟสสามารถกำหนดไว้ล่วงหน้าสำหรับคอมไพเลอร์; และ 2) ตัวรับและตัวตั้งค่าของตัวแปรสมาชิกนั้นง่ายต่อการทำให้เธรดปลอดภัยเนื่องจากการเปลี่ยนแปลงหรือการเข้าถึงค่าของพวกเขามักกระทำผ่าน 2 วิธีในคลาสพื้นฐานของวัตถุทั้งหมด 1) คลาสใด ๆ สามารถเรียกเมธอดใด ๆ บนคลาสอื่นโดยไม่ต้องรวมส่วนหัวหรือเขียนคลาสฐานปลอมเพื่อให้อินเตอร์เฟสสามารถกำหนดไว้ล่วงหน้าสำหรับคอมไพเลอร์ และ 2) ตัวรับและตัวตั้งค่าของตัวแปรสมาชิกนั้นง่ายต่อการทำให้เธรดปลอดภัยเนื่องจากการเปลี่ยนแปลงหรือการเข้าถึงค่าของพวกเขามักกระทำผ่าน 2 วิธีในคลาสพื้นฐานของวัตถุทั้งหมด 1) คลาสใด ๆ สามารถเรียกเมธอดใด ๆ บนคลาสอื่นโดยไม่ต้องรวมส่วนหัวหรือเขียนคลาสฐานปลอมเพื่อให้อินเตอร์เฟสสามารถกำหนดไว้ล่วงหน้าสำหรับคอมไพเลอร์ และ 2) ตัวรับและตัวตั้งค่าของตัวแปรสมาชิกนั้นง่ายต่อการทำให้เธรดปลอดภัยเนื่องจากการเปลี่ยนแปลงหรือการเข้าถึงค่าของพวกเขามักกระทำผ่าน 2 วิธีในคลาสพื้นฐานของวัตถุทั้งหมด

นอกจากนี้ยังนำไปสู่ความเป็นไปได้ในการทำสิ่งแปลก ๆ ที่ไม่ง่ายใน C ++ ตัวอย่างเช่นฉันสามารถสร้างวัตถุ Array ที่มีรายการใด ๆ รวมถึงตัวเองและสร้างอาร์เรย์ใหม่แบบไดนามิกโดยการส่งข้อความไปยังรายการอาร์เรย์ทั้งหมดและรวบรวมค่าตอบแทน (คล้ายกับแผนที่ใน Lisp) อีกประการหนึ่งคือการใช้งานการตรวจสอบคีย์ - ค่าโดยที่ฉันสามารถตั้งค่า UI เพื่อตอบสนองต่อการเปลี่ยนแปลงในสมาชิกของคลาสแบ็กเอนด์ทันทีแทนที่จะทำการสำรวจข้อมูลอย่างต่อเนื่อง

บางทีสิ่งที่น่าสนใจสำหรับคุณก็คือความจริงที่ว่าคุณสามารถถ่ายโอนวิธีการและสมาชิกทั้งหมดที่กำหนดไว้สำหรับชั้นเรียนและในรูปแบบสตริงไม่น้อย

ข้อเสียของระบบที่อาจขัดขวางคุณไม่ให้รบกวนการเพิ่มข้อความและคีย์ - ค่าทั้งหมดเป็นสิ่งที่น่าเบื่ออย่างยิ่ง มันช้ากว่าที่ไม่มีการสะท้อนใด ๆ คุณจะเกลียดที่จะเห็นboost::static_pointer_castและboost::dynamic_pointer_castทั่วฐานโค้ดของคุณด้วยความรุนแรง ข้อ จำกัด ของระบบที่พิมพ์อย่างรุนแรงยังคงอยู่ที่นั่นคุณเพียงซ่อนมันไว้เล็กน้อยดังนั้นจึงไม่ชัดเจน การพิมพ์ผิดในสายของคุณก็ไม่ใช่เรื่องสนุกหรือง่ายที่จะค้นพบความประหลาดใจ

สำหรับวิธีการใช้งานสิ่งนี้: เพียงใช้ตัวชี้ที่ใช้ร่วมกันและแบบอ่อนกับฐานทั่วไป ฉันขอแนะนำให้ติดตั้ง Boost.Function แทนที่จะทำอย่างที่ฉันทำซึ่งเป็นกับอึที่กำหนดเองและมาโครที่น่าเกลียดมากมายเพื่อตัดการเรียกฟังก์ชั่นของตัวชี้ เนื่องจากทุกอย่างถูกแมปแล้วการตรวจสอบวัตถุจึงเป็นเรื่องของการวนซ้ำผ่านคีย์ทั้งหมด เนื่องจากคลาสของฉันนั้นใกล้เคียงกับ ripoff โดยตรงของ Cocoa มากที่สุดเท่าที่จะเป็นไปได้โดยใช้ C ++ เท่านั้นหากคุณต้องการอะไรแบบนั้นฉันแนะนำให้ใช้เอกสาร Cocoa เป็นพิมพ์เขียว


เฮ้ @Michael; คุณยังมีซอร์สโค้ดสำหรับสิ่งนี้หรือคุณกำจัดมันทิ้งไป? ฉันอยากจะดูถ้าคุณไม่รังเกียจ
RandomDSdevel

อ๊ะสะกดชื่อคุณผิด! ไม่น่าแปลกใจที่ฉันไม่เคยได้รับคำตอบ ...
RandomDSdevel

10

มีอีกไลบรารีใหม่สำหรับการสะท้อนใน C ++ ที่เรียกว่าRTTR (การสะท้อนกลับประเภทเวลาเรียกใช้ดูที่GitHub ด้วย )

อินเทอร์เฟซคล้ายกับการสะท้อนใน C # และทำงานโดยไม่มี RTTI ใด ๆ


8

โซลูชันสองอย่างที่ฉันรู้จาก C ++ วันคือ:

1) ใช้ RTTI ซึ่งจะให้ bootstrap สำหรับคุณเพื่อสร้างพฤติกรรมคล้ายการสะท้อนของคุณถ้าคุณสามารถทำให้คลาสทั้งหมดของคุณได้รับมาจากคลาสพื้นฐาน 'วัตถุ' คลาสนั้นสามารถให้วิธีการบางอย่างเช่น GetMethod, GetBaseClass เป็นต้นสำหรับวิธีการทำงานของวิธีการเหล่านั้นคุณจะต้องเพิ่มมาโครบางตัวเพื่อตกแต่งประเภทของคุณด้วยตัวเองซึ่งเบื้องหลังจะสร้างข้อมูลเมตาในประเภทเพื่อให้คำตอบกับ GetMethods เป็นต้น

2) อีกตัวเลือกหนึ่งถ้าคุณมีการเข้าถึงคอมไพเลอร์วัตถุคือการใช้DIA SDK หากฉันจำอย่างถูกต้องสิ่งนี้จะช่วยให้คุณเปิด pdbs ซึ่งควรมีข้อมูลเมตาสำหรับประเภท C ++ ของคุณ มันอาจจะเพียงพอที่จะทำสิ่งที่คุณต้องการ หน้านี้แสดงวิธีการรับประเภทพื้นฐานทั้งหมดของคลาส

วิธีแก้ปัญหาทั้งสองนี้น่าเกลียดนิดหน่อย! ไม่มีอะไรที่เหมือนกับ C ++ นิดหน่อยที่จะทำให้คุณซาบซึ้งกับความหรูหราของ C #

โชคดี.


นั่นคือเจ้าเล่ห์และแฮ็คยักษ์ด้วยสิ่ง DIA SDK ที่คุณแนะนำนั่น
Sqeaky

7

แก้ไข: อัปเดตลิงก์ที่เสียหายเมื่อวันที่ 7 กุมภาพันธ์ 2017

ฉันคิดว่าไม่มีใครพูดถึงเรื่องนี้:

ที่ CERN พวกเขาใช้ระบบการสะท้อนเต็มรูปแบบสำหรับ C ++:

เซิร์น Reflex ดูเหมือนว่าจะทำงานได้ดีมาก


@ j4nbur53 ลิงก์เสียเนื่องจากดูเหมือนว่าพวกเขามาถึงขั้น: root.cern.ch
Germán Diago

มันอาจจะเป็นไปได้ว่าคุณหมายถึงการเชื่อมโยงนี้root.cern.ch/root/doc/ROOTUsersGuideHTML/ch07.htmlบทสะท้อน?
Mostowski ยุบ

ลองนี้root.cern.ch/how/how-use-reflex Reflex ทำงานเป็นเครื่องกำเนิดไฟฟ้าที่แยกวิเคราะห์ไฟล์ส่วนหัวของคุณและสร้างรหัส / ไลบรารีวิปัสสนา c ++ ที่คุณสามารถเชื่อมโยงและใช้ API อย่างง่าย
Adam Ryczkowski

6

ตอนนี้คำถามนี้ค่อนข้างเก่า (ไม่รู้ว่าทำไมฉันถึงตีคำถามเก่า ๆ ในวันนี้) แต่ฉันคิดถึงBOOST_FUSION_ADAPT_STRUCTที่แนะนำการสะท้อนเวลาแบบคอมไพล์

มันขึ้นอยู่กับคุณที่จะทำแผนที่สิ่งนี้เพื่อสะท้อนเวลาทำงานและมันจะไม่ง่ายเกินไป แต่เป็นไปได้ในทิศทางนี้ในขณะที่มันจะไม่กลับด้าน :)

ฉันคิดว่าแมโครเพื่อแค็ปซูลBOOST_FUSION_ADAPT_STRUCTหนึ่งสามารถสร้างวิธีการที่จำเป็นเพื่อให้ได้พฤติกรรมรันไทม์


2
โดย minghua (ผู้ที่แก้ไขโพสต์เดิม): ฉันขุดลงใน BOOST_FUSION_ADAPT_STRUCT โซลูชันนี้และในที่สุดก็มาพร้อมกับตัวอย่าง เห็นนี้ดังนั้นคำถามใหม่ - c ++ สำทับลงในช่อง struct ซ้อนกันกับ adapt_struct
Matthieu M.

เยี่ยมมาก Matthieu! เพิ่งรู้ว่าได้เห็นคำแนะนำของคุณที่นี่และในช่วงปีที่ผ่านมา ไม่ได้สังเกตว่าพวกเขาเกี่ยวข้องกันจนถึงตอนนี้ นั่นเป็นแรงบันดาลใจอย่างมาก
minghua

6

ฉันคิดว่าคุณอาจพบบทความที่น่าสนใจ "การใช้เทมเพลตสำหรับการสะท้อนใน C ++" โดย Dominic Filion มันเป็นในส่วน 1.4 ของการเขียนโปรแกรมเกมอัญมณี 5 น่าเสียดายที่ฉันไม่มีสำเนาของฉันมาด้วย แต่มองหามันเพราะฉันคิดว่ามันอธิบายสิ่งที่คุณขอ


4

Ponderเป็นไลบรารีสะท้อน C ++ เพื่อตอบคำถามนี้ ฉันพิจารณาตัวเลือกและตัดสินใจที่จะทำด้วยตัวเองเพราะฉันไม่สามารถหาตัวเลือกที่ทำเครื่องหมายในช่องทั้งหมดของฉัน

แม้ว่าจะมีคำตอบที่ดีสำหรับคำถามนี้ แต่ฉันไม่ต้องการใช้มาโครจำนวนมากหรือพึ่งพา Boost Boost เป็นห้องสมุดที่ยอดเยี่ยม แต่มีโครงการ C ++ 0x ขนาดเล็กจำนวนมากที่เรียบง่ายและมีเวลาในการรวบรวมที่เร็วขึ้น นอกจากนี้ยังมีข้อได้เปรียบในการตกแต่งชั้นเรียนภายนอกเช่นการห่อไลบรารี C ++ ที่ยังไม่สนับสนุน (C) 11 มันเป็นทางแยกของค่ายโดยใช้ C ++ 11 ที่ไม่ต้องเพิ่ม


4

การสะท้อนนั้นสำคัญเกี่ยวกับสิ่งที่คอมไพเลอร์ตัดสินใจทิ้งไว้เป็นรอยเท้าในโค้ดที่โค้ดรันไทม์สามารถสืบค้นได้ C ++ มีชื่อเสียงในการไม่จ่ายเงินสำหรับสิ่งที่คุณไม่ได้ใช้ เพราะคนส่วนใหญ่ไม่ได้ใช้ / ต้องการการไตร่ตรองคอมไพเลอร์ C ++ จะหลีกเลี่ยงค่าใช้จ่ายโดยไม่ต้องบันทึกอะไรเลย

ดังนั้น C ++ จึงไม่ได้สะท้อนและไม่ง่ายที่จะ "จำลอง" ด้วยตัวคุณเองตามกฎทั่วไปตามคำตอบอื่น ๆ

ภายใต้ "เทคนิคอื่น ๆ " หากคุณไม่มีภาษาที่มีการสะท้อนให้รับเครื่องมือที่สามารถดึงข้อมูลที่คุณต้องการในเวลารวบรวม

ชุดเครื่องมือการปรับรื้อซอฟต์แวร์ DMSของเรานั้นเป็นเทคโนโลยีคอมไพเลอร์ทั่วไปที่กำหนดพารามิเตอร์โดยคำจำกัดความที่ชัดเจน มันมีคำจำกัดความที่ไม่ดีสำหรับ C, C ++, Java, COBOL, PHP, ...

สำหรับเวอร์ชัน C, C ++, Java และ COBOL ให้การเข้าถึงที่สมบูรณ์สำหรับการแยกวิเคราะห์แผนผังต้นไม้และข้อมูลตารางสัญลักษณ์ ข้อมูลตารางสัญลักษณ์นั้นมีประเภทของข้อมูลที่คุณน่าจะต้องการจาก "การสะท้อน" หากเป้าหมายของคุณคือการระบุชุดของเขตข้อมูลหรือวิธีการและทำบางสิ่งกับพวกเขา DMS สามารถใช้ในการแปลงรหัสตามสิ่งที่คุณพบในตารางสัญลักษณ์ในรูปแบบโดยพลการ


3

คุณสามารถค้นหาห้องสมุดอื่นได้ที่นี่: http://www.garret.ru/cppreflection/docs/reflect.html รองรับ 2 วิธี: การรับข้อมูลประเภทจากข้อมูลการดีบักและให้โปรแกรมเมอร์เพื่อให้ข้อมูลนี้

ฉันยังสนใจในการไตร่ตรองโครงการของฉันและพบว่าห้องสมุดนี้ฉันยังไม่ได้ลองเลย แต่ลองใช้เครื่องมืออื่นจากคนนี้และฉันชอบวิธีการทำงาน :-)


3

ตรวจสอบ Classdesc http://classdesc.sf.net มันให้ภาพสะท้อนในรูปแบบของคลาส "descriptors" ทำงานร่วมกับคอมไพเลอร์ C ++ มาตรฐานใด ๆ (ใช่มันเป็นที่รู้จักกันในการทำงานกับ Visual Studio เช่นเดียวกับ GCC) และไม่จำเป็นต้องใช้คำอธิบายประกอบซอร์สโค้ด (แม้ว่า pragmas บางตัว ) มันได้รับการพัฒนามานานกว่าทศวรรษและใช้ในโครงการระดับอุตสาหกรรมจำนวนมาก


1
ยินดีต้อนรับสู่ Stack Overflow แม้ว่าคำตอบนี้อยู่ในหัวข้อมันเป็นสิ่งสำคัญที่จะต้องชี้ให้เห็นว่าคุณเป็นผู้เขียนซอฟต์แวร์นี้เพื่อให้ชัดเจนว่าไม่ใช่คำแนะนำที่เป็นกลาง :-)
Matthew Strawbridge

2

เมื่อฉันต้องการไตร่ตรองใน C ++ ฉันอ่านบทความนี้และปรับปรุงสิ่งที่ฉันเห็นที่นั่น ขออภัยไม่สามารถมีได้ ฉันไม่ได้เป็นเจ้าของผล ... แต่คุณสามารถได้รับสิ่งที่ฉันมีและไปจากที่นั่น

ขณะนี้ฉันกำลังค้นคว้าเมื่อฉันรู้สึกว่ามันเป็นวิธีการที่ใช้ inherit_linearly เพื่อให้คำจำกัดความของประเภทที่สะท้อนได้ง่ายขึ้นมาก จริง ๆ แล้วฉันได้ค่อนข้างไกล แต่ฉันก็ยังมีวิธีที่จะไป การเปลี่ยนแปลงใน C ++ 0x มีแนวโน้มว่าจะช่วยได้มากในพื้นที่นี้


2

ดูเหมือนว่า C ++ ยังไม่มีคุณสมบัตินี้ และC ++ 11เลื่อนการสะท้อนออกไปเช่นกัน ((

ค้นหามาโครหรือสร้างเอง Qt ยังสามารถช่วยสะท้อน (ถ้าสามารถใช้)


2

แม้ว่าการสะท้อนจะไม่ได้รับการสนับสนุนนอกกรอบใน c ++ แต่ก็ไม่ยากที่จะนำมาใช้ ฉันได้พบบทความที่ยอดเยี่ยมนี้: http://replicaisland.blogspot.co.il/2010/11/building-reflective-object-system-in-c.html

บทความอธิบายในรายละเอียดที่ดีว่าคุณจะสามารถใช้ระบบการสะท้อนที่เรียบง่ายและเป็นพื้นฐานได้อย่างไร ได้รับมันไม่ใช่วิธีการที่บริสุทธ์ที่สุดและมีขอบที่ขรุขระเหลือไว้ให้แยกออก แต่สำหรับความต้องการของฉันมันก็เพียงพอ

บรรทัดล่าง - การสะท้อนสามารถชำระได้ถ้าทำอย่างถูกต้องและเป็นไปได้อย่างสมบูรณ์ใน c ++


2

ฉันต้องการโฆษณาการมีอยู่ของชุดเครื่องมือวิปัสสนา / การสะท้อนอัตโนมัติ "IDK" มันใช้เมตาคอมไพเลอร์เหมือนของ Qt และเพิ่มข้อมูลเมตาลงในไฟล์วัตถุโดยตรง มันอ้างว่าใช้งานง่าย ไม่มีการอ้างอิงภายนอก มันยังช่วยให้คุณสามารถสะท้อนสตริง std :: โดยอัตโนมัติและใช้ในสคริปต์ โปรดดูIDK


2

หากคุณกำลังมองหาการสะท้อน C ++ ที่ค่อนข้างง่าย - ฉันได้รวบรวมมาโครจากแหล่งต่างๆ / กำหนดและให้ความเห็นว่ามันทำงานอย่างไร คุณสามารถดาวน์โหลดไฟล์ส่วนหัวได้จากที่นี่:

https://github.com/tapika/TestCppReflect/blob/master/MacroHelpers.h

ชุดของการกำหนดรวมทั้งฟังก์ชั่นด้านบน:

https://github.com/tapika/TestCppReflect/blob/master/CppReflect.h https://github.com/tapika/TestCppReflect/blob/master/CppReflect.cpp https://github.com/tapika/Tapika/Tapika/Tapika/TestCppReflect/ หยด / Master / TypeTraits.h

แอปพลิเคชันตัวอย่างอยู่ในที่เก็บ git เช่นกันที่นี่: https://github.com/tapika/TestCppReflect/

ฉันจะคัดลอกบางส่วนที่นี่พร้อมคำอธิบาย:

#include "CppReflect.h"
using namespace std;


class Person
{
public:

    // Repack your code into REFLECTABLE macro, in (<C++ Type>) <Field name>
    // form , like this:

    REFLECTABLE( Person,
        (CString)   name,
        (int)       age,
...
    )
};

void main(void)
{
    Person p;
    p.name = L"Roger";
    p.age = 37;
...

    // And here you can convert your class contents into xml form:

    CStringW xml = ToXML( &p );
    CStringW errors;

    People ppl2;

    // And here you convert from xml back to class:

    FromXml( &ppl2, xml, errors );
    CStringA xml2 = ToXML( &ppl2 );
    printf( xml2 );

}

REFLECTABLEdefine ใช้ชื่อคลาส + ชื่อฟิลด์ด้วยoffsetof- เพื่อระบุตำแหน่งที่อยู่ในฟิลด์เฉพาะของหน่วยความจำ ฉันพยายามเลือกคำศัพท์. NET ให้ได้มากที่สุดเท่าที่จะเป็นไปได้ แต่ C ++ และ C # นั้นแตกต่างกันดังนั้นจึงไม่ใช่ 1 ต่อ 1 ทั้งตัวแบบ C ++ ทั้งตัวสะท้อนอยู่ในTypeInfoและFieldInfoคลาส

ฉันใช้ pugi xml parser เพื่อดึงรหัสการสาธิตเป็น xml และกู้คืนจาก xml

ดังนั้นผลลัพธ์ที่ผลิตโดยรหัสตัวอย่างจะเป็นดังนี้:

<?xml version="1.0" encoding="utf-8"?>
<People groupName="Group1">
    <people>
        <Person name="Roger" age="37" />
        <Person name="Alice" age="27" />
        <Person name="Cindy" age="17" />
    </people>
</People>

นอกจากนี้ยังเป็นไปได้ที่จะเปิดใช้งานการสนับสนุนคลาส / โครงสร้างปาร์ตี้ของบุคคลที่ 3 ผ่านคลาส TypeTraits และข้อมูลจำเพาะเทมเพลตบางส่วน - เพื่อกำหนดคลาส TypeTraitsT ของคุณเองในลักษณะคล้ายกับ CString หรือ int - ดูตัวอย่างรหัสใน

https://github.com/tapika/TestCppReflect/blob/master/TypeTraits.h#L195

วิธีนี้ใช้ได้กับ Windows / Visual Studio เป็นไปได้ที่จะส่งพอร์ตไปยัง OS / คอมไพเลอร์อื่น ๆ แต่ยังไม่ได้ทำ (ถามฉันว่าคุณชอบวิธีแก้ปัญหาหรือไม่ฉันอาจช่วยคุณได้)

โซลูชันนี้ใช้สำหรับการถ่ายภาพต่อเนื่องเป็นอนุกรมของคลาสเดียวที่มีคลาสย่อยหลายคลาส

หากคุณกำลังค้นหากลไกในการทำให้ชิ้นส่วนของคลาสเป็นอนุกรมหรือเพื่อควบคุมการสะท้อนการทำงานที่เรียกว่าการผลิตคุณสามารถดูวิธีแก้ปัญหาต่อไปนี้:

https://github.com/tapika/cppscriptcore/tree/master/SolutionProjectModel

ข้อมูลรายละเอียดเพิ่มเติมสามารถพบได้จากวิดีโอ youtube:

การสะท้อนประเภท C ++ รันไทม์ https://youtu.be/TN8tJijkeFE

ฉันพยายามอธิบายให้ลึกซึ้งยิ่งขึ้นว่า c ++ reflection จะทำงานอย่างไร

โค้ดตัวอย่างจะมีลักษณะเช่นนี้:

https://github.com/tapika/cppscriptcore/blob/master/SolutionProjectModel/testCppApp.cpp

c.General.IntDir = LR"(obj\$(ProjectName)_$(Configuration)_$(Platform)\)";
c.General.OutDir = LR"(bin\$(Configuration)_$(Platform)\)";
c.General.UseDebugLibraries = true;
c.General.LinkIncremental = true;
c.CCpp.Optimization = optimization_Disabled;
c.Linker.System.SubSystem = subsystem_Console;
c.Linker.Debugging.GenerateDebugInformation = debuginfo_true;

แต่ในแต่ละขั้นตอนที่นี่จริงจะส่งผลในการเรียกฟังก์ชั่นการใช้ภาษา C ++ __declspec(property(get =, put ... )คุณสมบัติด้วย

ซึ่งได้รับข้อมูลเต็มรูปแบบเกี่ยวกับประเภทข้อมูล C ++ ชื่อคุณสมบัติ C ++ และพอยน์เตอร์อินสแตนซ์ของคลาสในรูปแบบของพา ธ และจากข้อมูลนั้นคุณสามารถสร้าง xml, json หรือแม้กระทั่งทำให้เป็นอันดับนั้นผ่านอินเทอร์เน็ต

ตัวอย่างของฟังก์ชันการโทรกลับเสมือนนั้นมีอยู่ที่นี่:

https://github.com/tapika/cppscriptcore/blob/master/SolutionProjectModel/VCConfiguration.cpp

ดูฟังก์ชั่นและฟังก์ชั่นเสมือนจริงReflectCopy::OnAfterSetProperty

แต่เนื่องจากหัวข้อขั้นสูงจริง ๆ ฉันขอแนะนำให้ตรวจสอบวิดีโอก่อน

หากคุณมีความคิดในการปรับปรุงบางอย่าลังเลที่จะติดต่อฉัน


1

การสะท้อนกลับใน C ++ นั้นมีประโยชน์มากในกรณีที่คุณจำเป็นต้องเรียกใช้เมธอดสำหรับสมาชิกแต่ละคน (ตัวอย่างเช่น: การทำให้เป็นอนุกรม, การแฮช, เปรียบเทียบ) ฉันมาพร้อมกับโซลูชันทั่วไปด้วยไวยากรณ์ที่ง่ายมาก:

struct S1
{
    ENUMERATE_MEMBERS(str,i);
    std::string str;
    int i;
};
struct S2
{
    ENUMERATE_MEMBERS(s1,i2);
    S1 s1;
    int i2;
};

โดยที่ ENUMERATE_MEMBERS เป็นมาโครซึ่งจะอธิบายภายหลัง (UPDATE):

สมมติว่าเราได้กำหนดฟังก์ชันการทำให้เป็นอนุกรมสำหรับ int และ std :: string ดังนี้:

void EnumerateWith(BinaryWriter & writer, int val)
{
    //store integer
    writer.WriteBuffer(&val, sizeof(int));
}
void EnumerateWith(BinaryWriter & writer, std::string val)
{
    //store string
    writer.WriteBuffer(val.c_str(), val.size());
}

และเรามีฟังก์ชั่นทั่วไปใกล้กับ "มาโครลับ";)

template<typename TWriter, typename T>
auto EnumerateWith(TWriter && writer, T && val) -> is_enumerable_t<T>
{
    val.EnumerateWith(write); //method generated by ENUMERATE_MEMBERS macro
}

ตอนนี้คุณสามารถเขียน

S1 s1;
S2 s2;
//....
BinaryWriter writer("serialized.bin");

EnumerateWith(writer, s1); //this will call EnumerateWith for all members of S1
EnumerateWith(writer, s2); //this will call EnumerateWith for all members of S2 and S2::s1 (recursively)

ดังนั้นการมีมาโคร ENUMERATE_MEMBERS ในคำจำกัดความของโครงสร้างคุณสามารถสร้างการทำให้เป็นอนุกรม, เปรียบเทียบ, hashing และเนื้อหาอื่น ๆ โดยไม่ต้องสัมผัสชนิดดั้งเดิมความต้องการเพียงอย่างเดียวคือการใช้วิธีการ "EnumerateWith" สำหรับแต่ละประเภทซึ่งไม่นับต่อ . โดยปกติแล้วคุณจะต้องใช้ประเภท 10-20 "วิ" เพื่อสนับสนุนประเภทใด ๆ ในโครงการของคุณ

แมโครนี้ควรมีศูนย์ค่าโสหุ้ยในการสร้าง / ทำลายในเวลาทำงานและรหัสของ T.EnumerateWith () ควรสร้างตามความต้องการซึ่งสามารถทำได้โดยการทำให้ฟังก์ชั่นแม่แบบอินไลน์ดังนั้นค่าใช้จ่ายเพียงอย่างเดียวใน เรื่องราวทั้งหมดคือการเพิ่ม ENUMERATE_MEMBERS (m1, m2, m3 ... ) ไปยังแต่ละ struct ในขณะที่การใช้วิธีการเฉพาะต่อประเภทสมาชิกนั้นเป็นสิ่งที่ต้องทำในการแก้ปัญหาใด ๆ ดังนั้นฉันจึงไม่ถือว่ามันเป็นค่าใช้จ่าย

อัปเดต: มีการใช้งานที่ง่ายมากของมาโคร ENUMERATE_MEMBERS (แต่อาจขยายได้เล็กน้อยเพื่อรองรับการสืบทอดจากโครงสร้างที่นับไม่ได้)

#define ENUMERATE_MEMBERS(...) \
template<typename TEnumerator> inline void EnumerateWith(TEnumerator & enumerator) const { EnumerateWithHelper(enumerator, __VA_ARGS__ ); }\
template<typename TEnumerator> inline void EnumerateWith(TEnumerator & enumerator) { EnumerateWithHelper(enumerator, __VA_ARGS__); }

// EnumerateWithHelper
template<typename TEnumerator, typename ...T> inline void EnumerateWithHelper(TEnumerator & enumerator, T &...v) 
{ 
    int x[] = { (EnumerateWith(enumerator, v), 1)... }; 
}

// Generic EnumerateWith
template<typename TEnumerator, typename T>
auto EnumerateWith(TEnumerator & enumerator, T & val) -> std::void_t<decltype(val.EnumerateWith(enumerator))>
{
    val.EnumerateWith(enumerator);
}

และคุณไม่จำเป็นต้องมีห้องสมุดบุคคลที่สามสำหรับรหัส 15 บรรทัดเหล่านี้;)


1

คุณสามารถใช้คุณสมบัติการสะท้อนคงที่ยอดเยี่ยมสำหรับโครงสร้างด้วยBOOST_HANA_DEFINE_STRUCTจากไลบรารี Boost :: Hana
ฮานะนั้นค่อนข้างหลากหลายไม่เพียง แต่สำหรับผู้ใช้ที่คุณนึกถึง แต่สำหรับเทมเพลตการเขียนโปรแกรมจำนวนมาก


1

Random Access สะท้อนทำให้ห้องสมุดค่อนข้างง่ายและสะท้อนให้เห็นถึงการใช้งานง่าย - ข้อมูลสาขา / ทุกชนิดถูกออกแบบมาเพื่อเป็นได้ทั้งที่มีอยู่ในอาร์เรย์หรือให้ความรู้สึกเหมือนการเข้าถึงอาร์เรย์ มันเขียนขึ้นสำหรับ C ++ 17 และทำงานร่วมกับ Visual Studios, g ++ และ Clang ไลบรารีเป็นส่วนหัวเท่านั้นหมายถึงคุณต้องการคัดลอก "Reflect.h" ลงในโครงการของคุณเพื่อใช้งานเท่านั้น

Structs ที่สะท้อนกลับหรือคลาสที่ต้องการมาโคร REFLECT ซึ่งคุณระบุชื่อของคลาสที่คุณสะท้อนและชื่อของฟิลด์

class FuelTank {
    public:
        float capacity;
        float currentLevel;
        float tickMarks[2];

    REFLECT(() FuelTank, () capacity, () currentLevel, () tickMarks)
};

นั่นคือทั้งหมดที่มีไม่มีรหัสเพิ่มเติมที่จำเป็นในการสะท้อนการตั้งค่า คุณสามารถจัดหาซูเปอร์คลาส (ในวงเล็บของอาร์กิวเมนต์แรก) และคำอธิบายประกอบฟิลด์ (ในวงเล็บนำหน้าฟิลด์ที่คุณต้องการใส่คำอธิบายประกอบ) เพื่อให้สามารถข้ามซูเปอร์คลาสหรือเพิ่มข้อมูลเวลารวบรวมเพิ่มเติมลงในฟิลด์ (เช่น Json: : ละเว้น)

การวนลูปผ่านฟิลด์สามารถทำได้ง่ายเหมือน ...

for ( size_t i=0; i<FuelTank::Class::TotalFields; i++ )
    std::cout << FuelTank::Class::Fields[i].name << std::endl;

คุณสามารถวนลูปผ่านอินสแตนซ์ของวัตถุเพื่อเข้าถึงค่าฟิลด์ (ซึ่งคุณสามารถอ่านหรือแก้ไข) และข้อมูลประเภทฟิลด์ ...

FuelTank::Class::ForEachField(fuelTank, [&](auto & field, auto & value) {
    using Type = typename std::remove_reference<decltype(value)>::type;
    std::cout << TypeToStr<Type>() << " " << field.name << ": " << value << std::endl;
});

JSON ห้องสมุดถูกสร้างขึ้นที่ด้านบนของ RandomAccessReflection ซึ่งระบุการแสดงผลผลิต JSON ที่เหมาะสมสำหรับการอ่านหรือเขียนอัตโนมัติและสามารถสำรวจซ้ำทุกสาขาสะท้อนให้เห็นเช่นเดียวกับอาร์เรย์และภาชนะบรรจุ STL

struct MyOtherObject { int myOtherInt; REFLECT(() MyOtherObject, () myOtherInt) };
struct MyObject
{
    int myInt;
    std::string myString;
    MyOtherObject myOtherObject;
    std::vector<int> myIntCollection;

    REFLECT(() MyObject, () myInt, () myString, (Reflected) myOtherObject, () myIntCollection)
};

int main()
{
    MyObject myObject = {};
    std::cout << "Enter MyObject:" << std::endl;
    std::cin >> Json::in(myObject);
    std::cout << std::endl << std::endl << "You entered:" << std::endl;
    std::cout << Json::pretty(myObject);
}

ด้านบนสามารถวิ่งได้เช่นนี้ ...

Enter MyObject:
{
  "myInt": 1337, "myString": "stringy", "myIntCollection": [2,4,6],
  "myOtherObject": {
    "myOtherInt": 9001
  }
}


You entered:
{
  "myInt": 1337,
  "myString": "stringy",
  "myOtherObject": {
    "myOtherInt": 9001
  },
  "myIntCollection": [ 2, 4, 6 ]
}

ดูสิ่งนี้ด้วย...


0

หากคุณประกาศตัวชี้ไปยังฟังก์ชันดังนี้:

int (*func)(int a, int b);

คุณสามารถกำหนดสถานที่ในหน่วยความจำให้กับฟังก์ชั่นเช่นนี้ (ต้องการlibdlและdlopen)

#include <dlfcn.h>

int main(void)
{
    void *handle;
    char *func_name = "bla_bla_bla";
    handle = dlopen("foo.so", RTLD_LAZY);
    *(void **)(&func) = dlsym(handle, func_name);
    return func(1,2);
}

หากต้องการโหลดสัญลักษณ์โลคัลโดยใช้ทางอ้อมคุณสามารถใช้dlopenกับการเรียกไบนารี ( argv[0])

ข้อกำหนดเพียงอย่างเดียวสำหรับสิ่งนี้ (นอกเหนือจากdlopen(), libdlและdlfcn.h) คือการรู้จักข้อโต้แย้งและประเภทของฟังก์ชัน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.