是否可以知道派生类的所有名称?

时间:2017-04-27 09:32:59

标签: c++ templates reflection

假设我们有一个基类和一堆派生类。有没有办法或机制以编程方式知道所有派生类名?

也许反射是一个好主意,但它不适用于C ++。我想在编译期间会有某种模板可以完成这项工作。

class Base{
public:
    virtual void print(){
        // This function should print all the names of derived class.
    }
    virtual Base* getInstance(string class_name){
        // This function should return an instance related to the class name.
    }
};

class Derived_1 : public Base{ // Suppose we have 100 Derived_X classes, 
                 // so we don't want to add its name to a list manually.
};

int main(){
    Base base;
    base.print(); // This should print the name of all the derived class.
    base.getInstance("Derived_1"); // This should return an instance of Derived_1
    return 0;
}

3 个答案:

答案 0 :(得分:1)

对于getInstance,您可以将其声明为模板(需要C ++ 14)。要获得程序中派生类的所有名称,您可能不得不求助于某些预处理程序。

#include <type_traits>

class Base
{
public:
    virtual ~Base () = default;

  template < typename T,
             typename = std::enable_if_t<std::is_base_of<Base, T>::value, void>
             >
    T getInstance() { return T{}; }
};

class Derived : public Base {};
class NotDerived {};


int main(){
    Base base;

    base.getInstance<Derived>();

    // error: no matching member function for call to 'getInstance'
    //base.getInstance<NotDerived>();
}

答案 1 :(得分:0)

关于派生类的名称,我提出了一个基于BaseList类/结构的解决方案,其中包含静态std::set(或其他容器)名称,模板Base class / struct,继承自BaseList并且其模板参数是派生类(CRTP样式)和(为了简化派生类/结构的构造,一个C风格的宏(我知道......宏是蒸馏的邪恶...但有时......)用必要的静态方法创建派生类/结构的声明,声明派生类/结构的名称,以及成员(激活名称的注册)

以下是一个完整的示例(遗憾的是C ++ 11)

#include <set>
#include <string>
#include <iostream>

struct BaseList
 {
   static std::set<std::string> const & derList (std::string const & dn)
    {
      static std::set<std::string> dl;

      if ( dn.size() )
         dl.insert(dn);

      return dl;
    }

   static void print ()
    {
      std::cout << "derived names: ";
      for ( auto const & dn : derList("") )
         std::cout << dn << ", ";
      std::cout << std::endl;
    }
 };

template <typename Der>
struct Base : public BaseList
 {
   static std::size_t setNameInList () 
    { return derList(Der::name()).size(); }

   static std::size_t id;
 };

template <typename Der>
std::size_t Base<Der>::id = setNameInList();

#define setDerived(nameDer) \
struct nameDer : public Base<nameDer>\
 { \
   std::size_t idc { id }; \
   static std::string name () \
    { return #nameDer; }

setDerived(Derived_1)
   // other elements
 };

setDerived(Derived_2)
   // other elements
 };

setDerived(Derived_3)
   // other elements
 };

int main()
 {
   BaseList::print();
 }

关于getInstance()问题,我能想象的唯一解决方案是Enry Menke(+1)提出的相同解决方案,所以我建议您通过模板类型参数获取实例。

答案 2 :(得分:0)

此解决方案基于以下事实:您实际上正在寻找工厂。它使用一个小宏来简化课程注册,希望你不要关心它。

<强> factory.h

#ifndef __FACTORY_H__
#define __FACTORY_H__

#include <map>
#include <functional>
#include <string>
#include <iostream>

template<class B>
class Factory {
  static std::map<std::string, std::function<B*()>> s_creators;

public:
  template<class T>
  static void registerClass(const std::string& name) {
    s_creators[name] = []() { return new T(); };
  }

  static B* create(const std::string& name) {
    const auto it = s_creators.find(name);
    if (it == s_creators.end()) return nullptr; // not a derived class
    return (it->second)();
  }

  static void printRegisteredClasses() {
    for (auto it = s_creators.begin(); it != s_creators.end(); ++it)
      std::cout << it->first << std::endl;
  }
};
template<class B> std::map<std::string, std::function<B*()>> Factory<B>::s_creators;

template<class B, class T>
class Creator {
public:
  Creator(const std::string& name) {
    Factory<B>::template registerClass<T>(name);
  }
};

#define REGISTER(base_class, derived_class) \
  Creator<base_class, derived_class> s_##derived_class##Creator(#derived_class);

#endif

<强> example.cpp

#include "factory.h"
#include <memory>

class Base {
public:
  virtual void printName() const { std::cout << "Base" << std::endl; }
};

class Derived1 : public Base {
public:
  virtual void printName() const override { std::cout << "Derived1" << std::endl; }
};
REGISTER(Base, Derived1);

class Derived2 : public Base {
public:
  virtual void printName() const override { std::cout << "Derived2" << std::endl; }
};
REGISTER(Base, Derived2);

int main() {
  std::cout << "Registered classes:" << std::endl;
  Factory<Base>::printRegisteredClasses();

  std::cout << "---" << std::endl;
  std::unique_ptr<Base> derived1(Factory<Base>::create("Derived1"));
  derived1->printName();

  return 0;
}

Notes :需要C ++ 11。我已经使用clang 3.9.0进行了测试,我还没有机会与其他编译器进行验证。