2012-05-28 2 views
0

У меня есть иерархия классов, базовый класс, имеющий функцию, чтобы напечатать имя класса:Можно ли прикрепить имя к классу runtime в C++?

#include <iostream> 
using namespace std; 

class base 
{ 
public: 
    virtual void print_name() { cout << typeid(*this).name() << endl; }; 
}; 

class derived1 : public base { }; 

class derived2 : public base { }; 

int main() 
{ 
    base Base; 
    Base.print_name(); 

    derived1 Derived1; 
    Derived1.print_name(); 

    derived2 Derived2; 
    Derived2.print_name(); 
} 

Выход из выше

class base 
class derived1 
class derived2 

, который, по сути, зависят от платформы.

Есть ли более или менее стандартный способ «прикрепить» к каждому классу уникальное имя, поэтому его можно использовать в printname(), делая вывод одинаковым для всех платформ (и независимо от изменений, внесенных в имена реальных классов) ?

+0

просто глупый вопрос: во сколько зависит эта платформа? – Walter

+0

Очень. Например, gcc не включает слово «класс» в результат (и может добавить что-то еще). В стандарте говорится: «Имена, правило кодирования и последовательность сортировки для типов все неопределенные и могут различаться между программами». –

ответ

2

Sure:

class base { 
    public: 
    virtual char const *name() const { return "base"; } 
}; 

class derived1 : public base { 
    public: 
    virtual char const *name() const { return "derived1"; } 
}; 

Однако, если вы не отменяют name в классе, его имя будет то, что его суперкласса. Это может быть ошибка или функция, в зависимости от вашего варианта использования. Если это ошибка, то вы можете добавить некоторые проверки во время выполнения, чтобы убедиться, что метод переопределяется:

virtual char const *name() const { 
    if (typeid(*this) != typeid(base)) 
     throw std::logic_error("name() not overridden"); 
    return "base"; 
} 

Но вам придется повторить эту проверку в каждом осуществлении name, которые должны быть переопределены.

+0

Спасибо. Но специфика моего дела заключается в том, что мне не разрешено изменять заголовки любого из производных классов, что необходимо, если я введу новую функцию для возврата имени.Тем не менее, я свободен делать что-либо с базовым классом (или, конечно, вводить любые новые классы). Что я могу сделать? –

+0

@ G-s: очень немного, боюсь. –

0

Вы можете использовать шаблон функции (или классы свойств), чтобы получить имена:

template<typename T> const char * ClassName(T const * objPtr); 

template<> const char * ClassName<derived1>(derived1 const *objPtr) { return "derived1"; } 
template<> const char * ClassName<derived2>(derived2 const *objPtr) { return "derived2"; } 

(Здесь параметр функция используется для параметра шаблона, соответствующего только). И в вызывающий код вы можете отдать свой указатель на определенный класс и использовать ClassName

derived1 * pDerived = static_cast<derived1*>(pPointer); 
cout << ClassName(pDerived); 

Однако все это время компиляции, и вам потребуется некоторый механизм (например, GUIDs), чтобы определить класс во время выполнения (бросить это к правильному типу указателя). Если у ваших классов нет никого, и вы не можете их изменить, я не могу вам помочь (но вы можете использовать технику признаков, чтобы локализовать ваш код выбора типа платформы.

+0

У него есть некоторые проблемы с динамическими значениями: как вы определяете реальный тип 'pPointer'? –

+0

Несомненно, вам нужно «точно знать» точный тип вашего pPointer. Скажем, если у классов автора есть некоторые перечисления, он мог бы использовать его для вывода правильного типа (и шаблоны могли бы помочь с этим). – Steed

1

Для этого можно эффективно использовать type_info.

type_info поддерживает before метод, соответствующем слабого порядка, что позволяет использовать его в std::map (например) до тех пор, как это предусмотрено задаваемого пользователем предикат.

struct TypeInfoLess { 
    bool operator()(std::type_info const* lhs, std::type_info const* rhs) const { 
     return lhs->before(rhs); 
    } 
}; 

struct AdditionalTypeInfo { 
    std::string name; 
}; 

typedef std::map<std::type_info const*, AdditionalTypeInfo, TypeInfoLess> TypeInfoMap; 

Затем, вы можете просто добавить/типы поиска:

template <typename T> 
void add(TypeInfoMap& map, T const& t, AdditionalTypeInfo const& ati) { 
    map[&typeid(t)] = ati; 
} 

template <typename T> 
AdditionalTypeInfo const* find(TypeInfoMap const& map, T const& t) { 
    TypeInfoMap::const_iterator it = map.find(&typeid(t)); 
    if (it == map.end()) { return 0; } 
    return &it->second; 
} 

int main() { 
    TypeInfoMap timap; 

    add(timap, timap, { "TypeInfoMap" }); 

    if (AdditionalTypeInfo const* const ati = find(timap, timap)) { 
     std::cout << ati->name << "\n"; 
    } 
} 

Примечание: именно тогда вы должны добавить на карту все типы, которые могут вам понадобиться.

+0

Кажется интересным. Этот материал для сопоставления имен можно объединить в специальный класс Singleton. Но что, если я хотел бы определить имена классов в разделах их реализации, а не в каком-то особом месте кода? Он может выглядеть как макросы формы SETNAME (TypeInfoMap, «TypeInfoMap»), которые должны добавить класс в список. И если в определении класса нет макросов, для него должно быть возвращено какое-то имя по умолчанию. Это можно сделать? –

+0

@ G-s: ну синглтон здесь переполнен, достаточно простого глобального, если вы действительно хотите спуститься по этой дороге; но я бы посоветовал это сделать. Регистрация, однако, не может быть действительно выполнена «в классе», вы можете вызвать метод как пар инициализации глобальной переменной (или локальной статики) для эмуляции этих механизмов реестра, но вы подвергаете себя фиаско порядка статического инициализации; так что это может стать болезненным. –

+0

Похоже, что следующий ответ в этом разделе, простое возвращение строки перегруженной функцией, дает возможность определять имена в разделах интерфейса классов. Есть ли у вас какие-либо недостатки по сравнению с вашим путем, если мне разрешено модифицировать производные классы так, как я хочу? По крайней мере, он не нуждается в регистрации классов в каком-то особом месте, которое кажется неудобным. –

Смежные вопросы