用C ++反映基类中的子类

时间:2013-11-26 07:18:46

标签: c++ reflection

我们希望C ++中的解决方案必须能够执行以下操作: 给定一个特定类型的字符串,让我们说'A',我们想要找到从'A'派生的所有类型。 从“A”派生的类型中实例化新对象。 例如。假设我们有一个类,VehicleEntity。 VehicleEntity有子类,PassangerCarEntity,TruckEntity,TrainEntity,BoatEntity。 我们不确定可能存在哪些车辆实体,因为可以添加包含更多VehicleEntities的库。例如。可以在部署后添加来自VehicleEntity的AirplaneEntity。 在应用程序中,当用户想要选择VehicleEntity时,用户应该能够选择从VehicleEntity派生的任何实体。这包括PassangerCarEntity,TruckEntity,TrainEntity,BoatEntity和AirplaneEntity。 用户选择一个实体,比如AirplaneEntity,必须实例化AirplaneEntity类型的新对象。

以下是C#中我们想要在C ++中实现的概念示例。 在C#中,可以按如下方式检索下拉列表的项目:

Type vehicleEntityType = typeof(VehicleEntity);
List<Type> types = new List<Type>();
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
    types.AddRange(assembly.GetTypes().Where(x => vehicleEntityType.IsAssignableFrom(x) && !x.IsGenericType && !x.IsAbstract));
dropDownBox.ItemList = types;

//To instantiate the object:
List<VehicleEntity> vehicleList = new List<VehicleEntity>();
Type newVehicleType = (Type)dropDownBox.SelectedItem;
object newVehicle = Activator.CreateInstance(newVehicleType ); // default constructor used, parameterless constructors for vehicles.
vehicleList.Add(newVehicle);

标准C ++似乎无法做到这一点,因为它不会在其对象上存储任何元数据。存在为C ++提供反射的外部库。 RTTI和boost.Mirror似乎无法做到这一点。 我确信我们不是唯一有这个问题并且存在解决方案的人。有什么解决方案来解决我们的问题?外部图书馆或其他手段。

5 个答案:

答案 0 :(得分:1)

使用反射来完成这样的任务对我来说似乎有点过分。从另一个角度看问题:你想知道,当某人来自你的班级时。这可以通过以下方式实现(但不限于):

  • 创建静态VehicleRepository类。
  • 准备以下功能:

    static VehicleContext * VehicleRepository::Register(string newName, std::function<Vehicle * (void)> vehicleCtor)
    

    此方法的任务是注册可以构建的新车型。

  • 在VehicleBase的基类'ctor中需要VehicleContext:

    VehicleBase::VehicleBase(VehicleContext * newContext)
    
  • 为派生类创建静态ctors,获取上下文:

    static VehicleContext * context;
    
    static NewVehicle()
    {
        context = VehicleBase::Register("NewVehicle", []() { return new NewVehicle(); });
    }
    

    C ++没有静态构造函数,但您可以通过以下方式模拟它们:Static constructor in c++。此外,此代码可以放在应用程序或库的某些初始化部分中。

  • 在实例化新派生类时使用上下文:

    NewVehicle::NewVehicle()
        : base(NewVehicle::context)
    {
    }
    

这是一种确保每辆车都能在VehicleRepository中正确注册的简单方法。您还将拥有所有已注册车辆的集中存储库以及正确的构造函数来创建它们。


通常,您必须创建遵循以下规则的架构:

如果类在实例化之前未在某个全局存储库中注册,则实例化派生类应该是不可能的。

上面介绍的另一种方法:

BaseVehicle::BaseVehicle(Token token)
{
    // token is passed from the derived class

    if (!GlobalRepository.IsRegistered(token))
        throw new std::exception("Vehicles should be registered prior to instantiating!");
}

令牌可以是类名,与类关联的GUID或其他内容。值得深思的思念:)

答案 1 :(得分:0)

这不是您想要的,但您可以在C ++中使用简单的基类。它确实使您能够检查基类。但是,它确实需要比C#更仔细的考虑和计划 - 正如那些评论你的帖子所建议的......

class Vehicle {
  public:
    virtual int move (void) = 0;
};

class Airplane : public Vehicle {
  public:
    int move (void);
    void autoPilot (void);
};

void GoFromMaineToSpainInVehicle (Vehicle *v);

int main (int argc, char *argv[]) {
    Vehicle *v = NULL;
    Airplane *cessna = new Airplane();

    // Request base class pointer
    v = dynamic_cast<Vehicle *>(cessna);

    if ( v ) { GoFromMaineToSpainInVehicle(v); }
    else { /*not a vehicle*/ }

    return 0;
}

答案 2 :(得分:0)

如果我们想要根据用户输入字符串获取对象组。我们在这里可以实现的是使用字符串输入修改工厂设计模式我们可以决定返回哪种类型的车辆对象。

这样的事情:

#include <iostream>
#include <boost/any.hpp>
#include <boost/unordered_map.hpp>
#include<list>

using namespace std;

boost::unordered_map<string, boost::any> objects;

class A{

  public:
    virtual void print()
    {
      cout<<"A"<<endl;
    }

};


class B:public A{

  public:

    void print()
    {
      cout<<"B"<<endl;
    }

};


class C:public A{

  public:

    void print()
    {
      cout<<"C"<<endl;
    }

};


class D{

  public:

    virtual void print()
    {
      cout<<"D"<<endl;
    }
};

class E:public D{

  public:

    void print()
    {
      cout<<"E"<<endl;
    }
};


class F:public D{


  public:

    void print()
    {
      cout<<"F"<<endl;
    }
};

boost::unordered_map<string, list<boost::any> > createInstance(string input)
{
  boost::unordered_map<string, list<boost::any> > list;
  if(input.compare("A") == 0 )
  {
    list[input].push_back(new A);
    list[input].push_back(new B);
    list[input].push_back(new C);
  }
  else if(input.compare("D") == 0 )
  {
    list[input].push_back(new D);
    list[input].push_back(new E);
    list[input].push_back(new F);
  }
  return list;
}



int main()
{

  string input;
  cout<<"input "<<endl;
  cin>>input;
  boost::unordered_map<string, list<boost::any> > objectList = createInstance(input);

  for(boost::unordered_map<string, list<boost::any> >::iterator itr = objectList.begin();
      itr!= objectList.end(); ++itr)
  { 
    for(list<boost::any>::iterator itrList = itr->second.begin();
        itrList!= itr->second.end();++itrList)
    {
      if((*itrList).type() == typeid(A*))
      (boost::any_cast<A*>(*itrList))->print();
      else if((*itrList).type() == typeid(B*))
      (boost::any_cast<B*>(*itrList))->print();
      else if((*itrList).type() == typeid(C*))
      (boost::any_cast<C*>(*itrList))->print();
      else if((*itrList).type() == typeid(D*))
      (boost::any_cast<D*>(*itrList))->print();
      else if((*itrList).type() == typeid(E*))
      (boost::any_cast<E*>(*itrList))->print();
      else if((*itrList).type() == typeid(F*))
      (boost::any_cast<F*>(*itrList))->print();
    }
  }
  return 0;
}

答案 3 :(得分:0)

您是否可以提供有关如何扩展可用类型列表的详细信息?

正如其他评论者所说,你不能以你在C ++中描述的方式实现反射。

我假设所有子类型都将在一些可再发行的库(或不同的库)中定义,这些库将与应用程序动态链接。在这种情况下,我认为图书馆应负责提供可用类型列表和一些工厂方法,以根据上下文实例化某些特定子类型。

答案 4 :(得分:0)

我找到了解决问题的两种方法。

第一个是通过反射库Reflex。它也被ROOT库使用(由CERN实验室开发,并积极维护。)

第二个,就是@Spook和@Eugene似乎想要的。 Cplusplus

感谢您的所有输入。