هل من الممكن الحصول على اسم * اسم القالب من نوع القالب في C ++

StackOverflow https://stackoverflow.com/questions/596185

  •  09-09-2019
  •  | 
  •  

سؤال

أريد أن أحصل على اسم السلسلة (const char *) من نوع القالب. لسوء الحظ، ليس لدي إمكانية الوصول إلى RTTI.

template< typename T >
struct SomeClass
{
    const char* GetClassName() const { return /* magic goes here */; }
}

وبالتالي

SomeClass<int> sc;
sc.GetClassName();   // returns "int"

هل هذا ممكن؟ لا أستطيع أن أجد وسيلة وأنا على وشك الاستسلام. شكرا للمساعدة.

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

المحلول

لا ولن يعمل موثوق به مع "نوع. سوف يمنحك بعض السلسلة الداخلية التي تعتمد على تنفيذ التحويل البرمجي. شيء مثل "int"، ولكن أيضا "أنا" شائع int.

بالمناسبة، إذا كان ما تريده هو مقارنة فقط ما إذا كان نوعان هو نفسه، لا تحتاج إلى تحويلها إلى سلسلة أولا. يمكنك فقط القيام به

template<typename A, typename B>
struct is_same { enum { value = false }; };

template<typename A>
struct is_same<A, A> { enum { value = true }; };

ثم تفعل

if(is_same<T, U>::value) { ... }

دفعة لديها بالفعل مثل هذا القالب، وسوف يكون معيار C ++ التالي std::is_same جدا.

التسجيل اليدوي للأنواع

يمكنك التخصص في أنواع مثل هذا:

template<typename> 
struct to_string {
    // optionally, add other information, like the size
    // of the string.
    static char const* value() { return "unknown"; }
};

#define DEF_TYPE(X) \
    template<> struct to_string<X> { \
        static char const* value() { return #X; } \
    }

DEF_TYPE(int); DEF_TYPE(bool); DEF_TYPE(char); ...

لذلك، يمكنك استخدامها مثل

char const *s = to_string<T>::value();

بالطبع، يمكنك أيضا التخلص من تعريف القالب الأساسي (والحفاظ على الإعلان الأمامي فقط) إذا كنت ترغب في الحصول على خطأ في وقت الترجمة إذا كان النوع غير معروف. أنا فقط تضمنت هنا للانتهاء.

لقد استخدمت البيانات الثابتة - أعضاء char const * سابقا، لكنها تسبب بعض المشاكل المعقدة، مثل الأسئلة التي تضع فيها إعلاناتهم، وهلم جرا. تخصصات الفئة مثل أعلاه حل المشكلة بسهولة.

تلقائي، اعتمادا على دول مجلس التعاون الخليجي

نهج آخر هو الاعتماد على المحول البرمجي الداخلي. في دول مجلس التعاون الخليجي، ما يلي يعطيني نتائج معقولة:

template<typename T>
std::string print_T() {
    return __PRETTY_FUNCTION__;
}

العودة ل std::string.

std::string print_T() [with T = std::basic_string<char, std::char_traits<char>, std::allocator<char> >]

بعض substr ماجيك intermixed مع find سوف أعطيك تمثيل السلسلة التي تبحث عنها.

نصائح أخرى

الحل السهل حقا: فقط اجتياز كائن سلسلة إلى منشئ SomeClass الذي يقول ما هو النوع.

مثال:

#define TO_STRING(type) #type
SomeClass<int> s(TO_STRING(int));

ببساطة تخزينها وعرضها في تنفيذ GetClassName.

حل أكثر تعقيدا قليلا، ولكن لا يزال سهلا للغاية:

#define DEC_SOMECLASS(T, name) SomeClass<T> name;  name.sType = #T; 

template< typename T >
struct SomeClass
{
    const char* GetClassName() const { return sType.c_str(); }
    std::string sType;
};


int main(int argc, char **argv)
{
    DEC_SOMECLASS(int, s);
    const char *p = s.GetClassName();

    return 0;
}

حل الحل غير النوعي:

يمكنك أيضا إجراء معرفات نوعك الخاص ولديك وظيفة للتحويل من وإلى الهوية وتمثيل السلسلة.

ثم يمكنك تمرير المعرف عند الإعلان عن النوع كمعلمة غير من النوع القالب:

template< typename T, int TYPEID>
struct SomeClass
{
    const char* GetClassName() const { return GetTypeIDString(TYPEID); }
};


...

SomeClass<std::string, STRING_ID> s1;
SomeClass<int, INT_ID> s2;

يمكنك تجربة شيء من هذا القبيل (تحذير هذا خارج الجزء العلوي فقط من رأسي، لذلك قد يكون هناك أخطاء في الترجمة، إلخ.)

template <typename T>
const char* GetTypeName()
{
    STATIC_ASSERT(0); // Not implemented for this type
}

#define STR(x) #x
#define GETTYPENAME(x) str(x) template <> const char* GetTypeName<x>() { return STR(x); }

// Add more as needed
GETTYPENAME(int)
GETTYPENAME(char)
GETTYPENAME(someclass)

template< typename T >
struct SomeClass
{
    const char* GetClassName() const { return GetTypeName<T>; }
}

هذا سيعمل لأي نوع تضيف GETTYPENAME(type) خط ل. لديها ميزة أنها تعمل دون تعديل الأنواع التي تهتم بها، وسوف تعمل مع أنواع المدمجة والمؤشرات. لديها العيب المميز الذي يجب عليك خط لكل نوع تريد استخدامه.

بدون استخدام RTTI المدمج، سيتعين عليك إضافة المعلومات الخاصة بك في مكان ما، إما إجابة Brian R. Bondy أو تعمل Dirkgently's. جنبا إلى جنب مع إجابتي، لديك ثلاث مواقع مختلفة لإضافة هذه المعلومات:

  1. في وقت إنشاء الكائن باستخدام SomeClass<int>("int")
  2. في الفصل باستخدام DIRKGELLY TIGLE-Time RTTI أو الوظائف الافتراضية
  3. مع القالب باستخدام الحل الخاص بي.

ستعمل الثلاثة جميعها، إنها مجرد مسألة ينتهي بك الأمر بأقل صداع صيانة في وضعك.

بواسطتك لا تملك الوصول إلى RTTI، فهل يعني أنه لا يمكنك استخدام "اسم" (t) .name ()؟ لأن هذه هي الطريقة الوحيدة فقط للقيام بذلك بمساعدة المحول البرمجي.

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

كل هذا يتوقف على ما ستفعله مع السلاسل، بالطبع.

يمكنك إضافة سحر قليلا نفسك. شيء مثل:

#include <iostream>

#define str(x) #x
#define xstr(x) str(x)
#define make_pre(C) concat(C, <)
#define make_post(t) concat(t, >)

#define make_type(C, T) make_pre(C) ## make_post(T)
#define CTTI_REFLECTION(T, x)  static std::string my_typeid() \
                               { return xstr(make_type(T, x)); }


// the dark magic of Compile Time Type Information (TM)
#define CTTI_REFLECTION(x)  static const char * my_typeid() \
                                  { return xstr(make_type(T, x)); }

#define CREATE_TEMPLATE(class_name, type) template<> \
                                    struct class_name <type>{ \
                                        CTTI_REFLECTION(class_name, type) \
                                    }; 

// dummy, we'll specialize from this later
template<typename T> struct test_reflection;

// create an actual class
CREATE_TEMPLATE(test_reflection, int)

struct test_reflection {
  CTTI_REFLECTION(test_reflection)
};

int main(int argc, char* argv[])
{
    std::cout << test_reflection<int>::my_typeid();
}

سأجعل المفتش static وظيفة (وبالتالي غيرconst).

لا آسف.

ولن يترجم RTTI حتى إذا حاولت استخدامه على INT.

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