سؤال

أود أن أكون قادرًا على استبطان فئة C++ لمعرفة اسمها ومحتوياتها (على سبيل المثال.الأعضاء وأنواعهم) الخ.أنا أتحدث هنا عن لغة C++ الأصلية، وليس لغة C++ المُدارة، والتي لها انعكاس.أدرك أن C++ توفر بعض المعلومات المحدودة باستخدام RTTI.ما هي المكتبات الإضافية (أو التقنيات الأخرى) التي يمكنها توفير هذه المعلومات؟

هل كانت مفيدة؟

المحلول 17

تأمل هي مكتبة انعكاس C++ للإجابة على هذا السؤال.لقد فكرت في الخيارات وقررت أن أصنع خيارًا خاصًا بي لأنني لم أتمكن من العثور على الخيار الذي يناسب كل ما عندي.

على الرغم من وجود إجابات رائعة على هذا السؤال، إلا أنني لا أرغب في استخدام الكثير من وحدات الماكرو، أو الاعتماد على Boost.تعد Boost مكتبة رائعة، ولكن هناك الكثير من المشاريع الصغيرة المخصصة لـ C++ 0x والتي تكون أبسط وتتمتع بأوقات تجميع أسرع.هناك أيضًا مزايا للقدرة على تزيين الفصل الدراسي خارجيًا، مثل تغليف مكتبة C++ التي لا تدعم C++11 (حتى الآن؟).إنه شوكة CAMP، باستخدام C++ 11، ذلك لم يعد يتطلب Boost.

نصائح أخرى

ما عليك القيام به هو جعل المعالج المسبق يقوم بإنشاء بيانات انعكاس حول الحقول.يمكن تخزين هذه البيانات كفئات متداخلة.

أولاً، لجعل كتابتها في المعالج المسبق أسهل وأنظف، سنستخدم التعبير المكتوب.التعبير المكتوب هو مجرد تعبير يضع النوع بين قوسين.لذا بدلاً من الكتابة 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 class، وذلك حتى يتمكن من الوصول إلى الحقول حتى عندما تكون خاصة:

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

وها هو، لقد قمنا للتو بتنفيذ الانعكاس في لغة C++، في أقل من 100 سطر من التعليمات البرمجية.

هناك نوعان من reflection السباحة حولها.

  1. التفتيش من خلال التكرار على أعضاء النوع وتعداد طرقه وما إلى ذلك.

    هذا غير ممكن مع C++.
  2. التفتيش عن طريق التحقق مما إذا كان نوع الفئة (الفئة، البنية، الاتحاد) لديه طريقة أو نوع متداخل، مشتق من نوع معين آخر.

    هذا النوع من الأشياء ممكن باستخدام C++ template-tricks.يستخدم boost::type_traits لأشياء كثيرة (مثل التحقق مما إذا كان النوع متكاملًا).للتحقق من وجود وظيفة عضو، استخدم هل من الممكن كتابة قالب للتحقق من وجود وظيفة؟ .للتحقق من وجود نوع متداخل معين، استخدم عادي سفيناي .

إذا كنت تبحث عن طرق لإنجاز 1)، مثل البحث عن عدد الأساليب التي يمتلكها الفصل الدراسي، أو الحصول على تمثيل السلسلة لمعرف الفصل، فأنا أخشى أنه لا توجد طريقة قياسية C++ للقيام بذلك.عليك استخدام أي منهما

  • مترجم Meta مثل Qt Meta Object Compiler الذي يترجم التعليمات البرمجية الخاصة بك بإضافة معلومات تعريف إضافية.
  • إطار عمل يتكون من وحدات ماكرو تسمح لك بإضافة المعلومات الوصفية المطلوبة.ستحتاج إلى إخبار إطار العمل بجميع الأساليب وأسماء الفئات والفئات الأساسية وكل ما يحتاجه.

تم تصميم لغة C++ مع أخذ السرعة في الاعتبار.إذا كنت تريد فحصًا عالي المستوى، مثل C# أو Java، فأنا أخشى أن أخبرك أنه لا توجد طريقة دون بذل بعض الجهد.

وأنا أحب المهر، ولكن المهور ليست مجانية.:-ص

http://en.wikibooks.org/wiki/C%2B%2B_Programming/RTTI هو ما ستحصل عليه.الانعكاس الذي تفكر فيه - البيانات الوصفية الكاملة المتوفرة في وقت التشغيل - غير موجود لـ C++ افتراضيًا.

RTTI غير موجود لـ C++.

وهذا ببساطة خطأ.في الواقع، تمت صياغة مصطلح "RTTI" وفقًا لمعيار C++.من ناحية أخرى، لا يذهب RTTI إلى حد بعيد في تنفيذ التفكير.

المعلومات موجودة بالفعل - ولكن ليس بالتنسيق الذي تحتاجه، وفقط إذا قمت بتصدير فصولك الدراسية.هذا يعمل على نظام 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);

تحتوي الدالة Invoc(this_pointer,...) على وسائط متغيرة.من الواضح أنه من خلال استدعاء دالة بهذه الطريقة، فإنك تتحايل على أشياء مثل const-safety وما إلى ذلك، لذلك يتم تنفيذ هذه الجوانب كفحوصات وقت التشغيل.

أنا متأكد من أنه يمكن تحسين بناء الجملة، وهو يعمل فقط على Win32 وWin64 حتى الآن.لقد وجدنا أنه من المفيد حقًا وجود واجهات GUI تلقائية للفئات، وإنشاء خصائص في C++، والتدفق من وإلى XML وما إلى ذلك، وليس هناك حاجة للاشتقاق من فئة أساسية محددة.إذا كان هناك ما يكفي من الطلب ربما يمكننا أن نجعله جاهزًا للإصدار.

أنت بحاجة إلى إلقاء نظرة على ما تحاول القيام به، وما إذا كانت 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, ، تتم ترجمة هذا إلى سلسلة من استدعاءات الأساليب في وقت التشغيل (عند قراءة ملف التكوين)، لذلك فهو فعال إلى حد ما.لا أوصي بالقيام بذلك إلا إذا كنت بحاجة لذلك حقًا، ولكن عندما تفعل ذلك، يمكنك القيام ببعض الأشياء الرائعة حقًا.

ماذا تحاول أن تفعل بالتفكير؟
يمكنك استخدام التعزيز سمات النوع و نوع من المكتبات كشكل محدود من التفكير في وقت الترجمة.أي أنه يمكنك فحص وتعديل الخصائص الأساسية للنوع الذي تم تمريره إلى القالب.

أوصي باستخدام كيو تي.

هناك ترخيص مفتوح المصدر بالإضافة إلى ترخيص تجاري.

يحرر: معسكر لم يعد يتم صيانته؛تتوفر شوكتان:

  • ويسمى واحد أيضا معسكر أيضًا، ويعتمد على نفس واجهة برمجة التطبيقات.
  • تأمل عبارة عن إعادة كتابة جزئية، ويجب تفضيلها لأنها لا تتطلب Boost ؛إنه يستخدم C++ 11.

معسكر هي مكتبة مرخصة من معهد ماساتشوستس للتكنولوجيا (LGPL سابقًا) تضيف انعكاسًا إلى لغة C++.لا يتطلب الأمر خطوة معالجة مسبقة محددة في التجميع، ولكن يجب أن يتم الربط يدويًا.

تستخدم مكتبة Tegesoft الحالية Boost، ولكن هناك أيضًا شوكة باستخدام C++ 11 ذلك لم يعد يتطلب Boost.

لقد فعلت شيئًا مثل ما تسعى إليه مرة واحدة، وعلى الرغم من أنه من الممكن الحصول على مستوى معين من التفكير والوصول إلى ميزات ذات مستوى أعلى، إلا أن صداع الصيانة قد لا يستحق كل هذا العناء.تم استخدام نظامي لإبقاء فئات واجهة المستخدم منفصلة تمامًا عن منطق الأعمال من خلال التفويض المشابه لمفهوم Objective-C لتمرير الرسائل وإعادة توجيهها.تتمثل طريقة القيام بذلك في إنشاء فئة أساسية قادرة على تعيين الرموز (لقد استخدمت مجموعة سلاسل ولكن يمكنك القيام بذلك باستخدام التعدادات إذا كنت تفضل معالجة الأخطاء في السرعة ووقت الترجمة على المرونة الكاملة) لمؤشرات الوظيفة (في الواقع لا مؤشرات وظيفية خالصة، ولكن شيئًا مشابهًا لما يمتلكه Boost مع Boost.Function - والذي لم أتمكن من الوصول إليه في ذلك الوقت).يمكنك أن تفعل الشيء نفسه بالنسبة لمتغيرات أعضائك طالما أن لديك فئة أساسية مشتركة قادرة على تمثيل أي قيمة.كان النظام بأكمله عبارة عن احتيال بلا خجل لتشفير القيمة الأساسية والتفويض، مع بعض الآثار الجانبية التي ربما كانت تستحق الوقت الهائل اللازم لجعل كل فصل يستخدم النظام يطابق جميع أساليبه وأعضائه مع المكالمات القانونية :1) يمكن لأي فئة استدعاء أي طريقة على أي فئة أخرى دون الحاجة إلى تضمين رؤوس أو كتابة فئات أساسية مزيفة بحيث يمكن تعريف الواجهة مسبقًا للمترجم؛و 2) كان من السهل جعل الحروف وأدوات ضبط متغيرات الأعضاء آمنة لمؤشر الترابط لأن تغيير قيمها أو الوصول إليها كان يتم دائمًا من خلال طريقتين في الفئة الأساسية لجميع الكائنات.

كما أدى ذلك إلى إمكانية القيام ببعض الأشياء الغريبة حقًا والتي ليست سهلة في لغة C++.على سبيل المثال، يمكنني إنشاء كائن مصفوفة يحتوي على عناصر عشوائية من أي نوع، بما في ذلك نفسه، وإنشاء مصفوفات جديدة ديناميكيًا عن طريق تمرير رسالة إلى جميع عناصر المصفوفة وجمع قيم الإرجاع (على غرار الخريطة في Lisp).وكان آخرها هو تنفيذ مراقبة القيمة الرئيسية، حيث تمكنت من إعداد واجهة المستخدم للاستجابة فورًا للتغيرات في أعضاء فئات الواجهة الخلفية بدلاً من استقصاء البيانات باستمرار أو إعادة رسم العرض دون داع.

ربما الأكثر إثارة للاهتمام بالنسبة لك هو حقيقة أنه يمكنك أيضًا تفريغ كافة الأساليب والأعضاء المحددين لفئة ما، وفي شكل سلسلة ليس أقل من ذلك.

سلبيات النظام التي قد تثنيك عن الإزعاج:إن إضافة كافة الرسائل والقيم الأساسية أمر ممل للغاية؛إنه أبطأ من دون أي انعكاس؛سوف تنمو لتكره الرؤية boost::static_pointer_cast و boost::dynamic_pointer_cast في جميع أنحاء قاعدة التعليمات البرمجية الخاصة بك مع شغف عنيف؛لا تزال القيود المفروضة على النظام المكتوب بقوة موجودة، فأنت في الواقع تخفيها قليلاً حتى لا يكون الأمر واضحًا.الأخطاء المطبعية في سلاسلك ليست أيضًا مفاجأة ممتعة أو من السهل اكتشافها.

بالنسبة لكيفية تنفيذ شيء مثل هذا:ما عليك سوى استخدام المؤشرات المشتركة والضعيفة لبعض القواعد المشتركة (كان يُطلق على مؤشراتي اسم "الكائن" بشكل خيالي للغاية) واشتقاق جميع الأنواع التي تريد استخدامها.أوصي بتثبيت Boost.Function بدلاً من القيام بذلك بالطريقة التي قمت بها، والتي كانت تتضمن بعض الحماقات المخصصة والكثير من وحدات الماكرو القبيحة لتغطية استدعاءات مؤشر الوظيفة.نظرًا لأنه تم تعيين كل شيء، فإن فحص الكائنات هو مجرد مسألة تكرار عبر جميع المفاتيح.نظرًا لأن فصولي كانت في الأساس قريبة من الغش المباشر لـ Cocoa قدر الإمكان باستخدام C ++ فقط، إذا كنت تريد شيئًا كهذا، فأنا أقترح استخدام وثائق Cocoa كمخطط.

الحلان الشبيهان بالانعكاس اللذان أعرفهما منذ أيام C++ هما:

1) استخدم RTTI، الذي سيوفر لك تمهيدًا لبناء سلوكك المشابه للانعكاس، إذا كنت قادرًا على جعل جميع فئاتك مستمدة من فئة أساسية "للكائن".يمكن أن توفر هذه الفئة بعض الأساليب مثل GetMethod وGetBaseClass وما إلى ذلك.أما بالنسبة لكيفية عمل هذه الأساليب، فستحتاج إلى إضافة بعض وحدات الماكرو يدويًا لتزيين أنواعك، والتي تعمل خلف الكواليس على إنشاء بيانات تعريف في النوع لتقديم إجابات لـ GetMethods وما إلى ذلك.

2) هناك خيار آخر، إذا كان لديك حق الوصول إلى كائنات المترجم، وهو استخدام ملف ديا SDK.إذا كنت أتذكر بشكل صحيح، فهذا يتيح لك فتح pdbs، والذي يجب أن يحتوي على بيانات تعريف لأنواع C++ الخاصة بك.قد يكون كافيا للقيام بما تحتاجه. هذه الصفحة يوضح كيف يمكنك الحصول على جميع الأنواع الأساسية للفصل على سبيل المثال.

كلا الحلين قبيحان بعض الشيء!لا يوجد شيء مثل القليل من لغة C++ لتجعلك تقدر رفاهية لغة C#.

حظ سعيد.

توجد مكتبة جديدة أخرى للتأمل في لغة C++ تسمى رتر (تشغيل انعكاس نوع الوقت، راجع أيضًا جيثب).

الواجهة مشابهة للانعكاس في C# وتعمل بدون أي RTTI.

يحرر:تم تحديث الرابط المعطل اعتبارًا من 7 فبراير 2017.

أعتقد أن أحدا لم يذكر هذا:

في CERN يستخدمون نظام الانعكاس الكامل لـ C++:

منعكس سيرن.ويبدو أن تعمل بشكل جيد جدا.

لا يتم دعم الانعكاس بواسطة C++ خارج الصندوق.هذا أمر محزن لأنه يجعل الاختبار الدفاعي مؤلمًا.

هناك عدة طرق للقيام بالتفكير:

  1. استخدم معلومات التصحيح (غير المحمولة).
  2. رش التعليمات البرمجية الخاصة بك باستخدام وحدات الماكرو/القوالب أو بعض أساليب المصدر الأخرى (تبدو قبيحة)
  3. قم بتعديل مترجم مثل clang/gcc لإنتاج قاعدة بيانات.
  4. استخدم نهج كيو تي موك
  5. تعزيز تعكس
  6. انعكاس دقيق ومسطح

يبدو الرابط الأول هو الأكثر واعدة (يستخدم التعديلات للربط)، ويناقش الثاني عددًا من التقنيات، والثالث عبارة عن نهج مختلف باستخدام 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/2017

يبدو أن الانعكاس يتجه نحو C++ 20 أو على الأرجح TSR.لكن الحركة بطيئة.

تحرير 15/09/2018

وقد تم إرسال مسودة الاستراتيجية الفنية إلى الهيئات الوطنية للتصويت عليها.

يمكن العثور على النص هنا: https://github.com/cplusplus/reflection-ts

هذا السؤال قديم بعض الشيء الآن (لا أعرف لماذا أستمر في طرح الأسئلة القديمة اليوم) ولكني كنت أفكر فيه BOOST_FUSION_ADAPT_STRUCT الذي يقدم انعكاس وقت الترجمة.

الأمر متروك لك لتخطيط هذا للانعكاس في وقت التشغيل بالطبع، ولن يكون الأمر سهلاً للغاية، ولكنه ممكن في هذا الاتجاه، في حين أنه لن يكون في الاتجاه المعاكس :)

أعتقد حقا أن الماكرو لتغليف BOOST_FUSION_ADAPT_STRUCT يمكن للمرء إنشاء الأساليب اللازمة للحصول على سلوك وقت التشغيل.

أعتقد أنك قد تجد مقالة مثيرة للاهتمام بعنوان "استخدام قوالب الانعكاس في لغة C++" بقلم دومينيك فيليون.وهو موجود في القسم 1.4 من برمجة لعبة الجواهر 5.لسوء الحظ، ليس لدي نسختي معي، ولكن ابحث عنها لأنني أعتقد أنها تشرح ما تطلبه.

يدور الانعكاس بشكل أساسي حول ما قرر المترجم تركه كآثار في الكود الذي يمكن لرمز وقت التشغيل الاستعلام عنه.تشتهر لغة C++ بعدم الدفع مقابل ما لا تستخدمه؛نظرًا لأن معظم الأشخاص لا يستخدمون/لا يريدون التفكير، فإن مترجم C++ يتجنب التكلفة من خلال عدم التسجيل أي شئ.

لذلك، لا يوفر C++ انعكاسًا، وليس من السهل "محاكاته" بنفسك كقاعدة عامة كما لاحظت الإجابات الأخرى.

ضمن "تقنيات أخرى"، إذا لم يكن لديك لغة ذات انعكاس، احصل على أداة يمكنها استخراج المعلومات التي تريدها في وقت الترجمة.

ملكنا مجموعة أدوات إعادة هندسة برمجيات DMS هي تقنية مترجم معممة يتم تحديد معلماتها بواسطة تعريفات لغة واضحة.يحتوي على تعريفات لغة لـ C وC++ وJava وCOBOL وPHP و...

بالنسبة لإصدارات C وC++ وJava وCOBOL، فإنه يوفر وصولاً كاملاً إلى أشجار التحليل ومعلومات جدول الرموز.تتضمن معلومات جدول الرموز نوع البيانات التي من المحتمل أن تريدها من "الانعكاس".إذا كان هدفك هو تعداد مجموعة من الحقول أو الأساليب و يفعل شيء معهم، يمكن استخدام DMS لتحويل الكود وفقًا لما تجده في جداول الرموز بطرق عشوائية.

يمكنك العثور على مكتبة أخرى هنا: http://www.garret.ru/cppreflection/docs/reflect.htmlوهو يدعم طريقتين:الحصول على معلومات النوع من معلومات التصحيح والسماح للمبرمج بتقديم هذه المعلومات.

أنا أيضًا مهتم بالتفكير في مشروعي ووجدت هذه المكتبة، لم أجرّبها بعد، لكني جربت أدوات أخرى من هذا الرجل وأحب طريقة عملها :-)

راجع كلاسديسك http://classdesc.sf.net.إنه يوفر انعكاسًا في شكل "واصفات" للفئة، ويعمل مع أي مترجم C++ قياسي (نعم من المعروف أنه يعمل مع Visual Studio بالإضافة إلى دول مجلس التعاون الخليجي)، ولا يتطلب تعليقًا توضيحيًا للكود المصدري (على الرغم من وجود بعض البراغما للتعامل مع المواقف الصعبة ).لقد كان قيد التطوير لأكثر من عقد من الزمان، ويستخدم في عدد من المشاريع الصناعية.

عندما أردت التفكير في C++ قرأت هذا المقال وتحسين ما رأيته هناك.آسف، لا يمكن أن يكون.أنا لا أملك النتيجة... ولكن يمكنك بالتأكيد الحصول على ما حصلت عليه والبدء من هناك.

أقوم حاليًا بالبحث، عندما أرغب في ذلك، عن طرق لاستخدام inherit_linearly لجعل تعريف الأنواع القابلة للانعكاس أسهل بكثير.لقد قطعت شوطا طويلا في هذا الأمر في الواقع ولكن لا يزال لدي طريق لأقطعه.من المحتمل جدًا أن تكون التغييرات في C++0x مفيدة جدًا في هذا المجال.

يبدو أن لغة C++ لا تزال لا تحتوي على هذه الميزة.و سي++11 تأجيل التفكير أيضا ((

ابحث في بعض وحدات الماكرو أو اصنعها بنفسك.يمكن أن تساعد Qt أيضًا في التفكير (إذا كان من الممكن استخدامها).

حاول أن تنظر إلى هذا المشروع http://www.garret.ru/cppreflection/docs/reflect.htmlتمت إضافة انعكاسات إلى C++.لقد أضاف بيانات التعريف إلى الفئات التي يمكنك استخدامها بعد ذلك.

على الرغم من أن الانعكاس غير مدعوم في لغة c++، إلا أنه ليس من الصعب تنفيذه.لقد واجهت هذا المقال الرائع:http://replicaisland.blogspot.co.il/2010/11/building-reflective-object-system-in-c.html

تشرح المقالة بتفصيل كبير كيف يمكنك تنفيذ نظام انعكاس بسيط جدًا وبدائي.نظرًا لأنه ليس الحل الأكثر فائدة، وهناك حواف خشنة متبقية يجب حلها، لكنها كانت كافية لاحتياجاتي.

خلاصة القول - يمكن للتفكير أن يؤتي ثماره إذا تم إجراؤه بشكل صحيح، وهو ممكن تمامًا في لغة c++.

أرغب في الإعلان عن وجود مجموعة أدوات الاستبطان/التأمل التلقائي "IDK".يستخدم مترجمًا تعريفيًا مثل Qt ويضيف معلومات التعريف مباشرة إلى ملفات الكائنات.يُزعم أنه سهل الاستخدام.لا تبعيات خارجية.حتى أنه يسمح لك بعكس std::string تلقائيًا ثم استخدامه في البرامج النصية.رجاءا إنظر على لا أعلم

يعد الانعكاس في لغة 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 في تعريف البنية، يمكنك إنشاء التسلسل والمقارنة والتجزئة وغيرها من العناصر دون لمس النوع الأصلي، والشرط الوحيد هو تنفيذ طريقة "EnumerateWith" لكل نوع، وهو غير قابل للتعداد، لكل عداد (مثل BinaryWriter) .سيتعين عليك عادةً تنفيذ 10-20 نوعًا "بسيطًا" لدعم أي نوع في مشروعك.

يجب أن يكون لهذا الماكرو حمل صفري لإنشاء/تدمير البنية في وقت التشغيل، ويجب إنشاء كود T.EnumerateWith() عند الطلب، وهو ما يمكن تحقيقه عن طريق جعله وظيفة مضمنة في القالب، وبالتالي فإن الحمل الوحيد في القصة كلها هي إضافة ENUMERATE_MEMBERS(m1,m2,m3...) إلى كل بنية، بينما يعد تنفيذ طريقة محددة لكل نوع عضو أمرًا ضروريًا في أي حل، لذلك لا أفترض أنه عبء إضافي.

تحديث:يوجد تطبيق بسيط جدًا للماكرو 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);
}

ولا تحتاج إلى أي مكتبة خارجية لهذه الأسطر الخمسة عشر من التعليمات البرمجية؛)

مع C++20، حصلت على بيانات التوسع, ، والذي يسمح لك بالتكرار على أنواع المجاميع:

struct my_type {
    double data;
    std::string another_data;
    int last_data;
};

auto object = my_type{};

for...(auto& member : object) {
    using member_type = std::remove_cvref_t<decltype(member)>;
    member = get_data<member_type>();
}

إذا كنت تبحث عن انعكاس بسيط نسبيًا لـ 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/TestCppReflect/blob/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 );

}

REFLECTABLE تعريف يستخدم اسم الفئة + اسم الحقل مع offsetof - لتحديد المكان الذي يقع فيه حقل معين في الذاكرة.لقد حاولت التقاط مصطلحات .NET قدر الإمكان، لكن C++ وC# مختلفان، لذا فالأمر ليس 1 إلى 1.يوجد نموذج انعكاس C++ بالكامل TypeInfo و FieldInfo الطبقات.

لقد استخدمت محلل Pugi XML لجلب الكود التجريبي إلى 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>

من الممكن أيضًا تمكين أي فئة / دعم هيكل تابع لجهة خارجية عبر فئة TypeTraits ومواصفات القالب الجزئي - لتحديد فئة TypeTraitsT الخاصة بك، بطريقة مشابهة لـ CString أو int - راجع رمز المثال في

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

ينطبق هذا الحل على Windows / Visual studio.من الممكن نقله إلى أنظمة تشغيل/مترجمين آخرين، لكن لم يتم ذلك.(اسألني إذا كنت تحب الحل حقًا، فقد أكون قادرًا على مساعدتك)

ينطبق هذا الحل على تسلسل طلقة واحدة لفئة واحدة مع فئات فرعية متعددة.

ومع ذلك، إذا كنت تبحث عن آلية لإجراء تسلسل لأجزاء الفئة أو حتى للتحكم في ما تنتجه استدعاءات انعكاس الوظيفة، فيمكنك إلقاء نظرة على الحل التالي:

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

يمكن العثور على مزيد من المعلومات التفصيلية من فيديو يوتيوب:

انعكاس نوع وقت التشغيل C++https://youtu.be/TN8tJijkeFE

أحاول أن أشرح بشكل أعمق قليلاً كيفية عمل انعكاس لغة c++.

سيبدو نموذج التعليمات البرمجية كما يلي على سبيل المثال:

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.

ولكن نظرًا لأن الموضوع متقدم حقًا - أوصي بمراجعة الفيديو أولاً.

إذا كان لديك بعض أفكار التحسين، فلا تتردد في الاتصال بي.

يدعم مشروع Root Reflex هذا الأمر.

يرى https://root.cern.ch/how/how-use-reflex

إذا قمت بتعريف مؤشر لوظيفة مثل هذا:

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) هو معرفة الوسائط ونوع الوظيفة.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top