0

We want a solution in C++ that must be able to do the following: Given a string of particular type, lets say 'A', we want to find all the types that derives from 'A'. Instantiate new objects out of the types that are derived from 'A'. E.g. Lets say we have a class, VehicleEntity. VehicleEntityhas child classes, PassangerCarEntity, TruckEntity, TrainEntity, BoatEntity. We are unsure what vehicle entities there may be as the a library could be added containing more VehicleEntities. E.g. an AirplaneEntity thaterives from VehicleEntity could be added after deployment. In the application, when a user wants to select a VehicleEntity, the user should be able to pick any of the entities deriving from VehicleEntity. This includes the PassangerCarEntity, TruckEntity, TrainEntity, BoatEntity and AirplaneEntity. The user selects an Entity, lets say AirplaneEntity, A new object of type AirplaneEntity must be instantiated.

The following is an concept example in C# of what we want to achieve in C++. In C# the items for the dropdown list can be retrieved as follows:

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);

Standard C++ seems unable to do this as it does not store any metadata on its objects. There exist external libraries that provides reflection to C++. RTTI and boost.Mirror seems unable to do this. I am sure that we are not the only ones that had this problem and that solutions exist. What solutions exist to address our problem? External libraries or other means.

user3035260
  • 105
  • 1
  • 9
  • 4
    In my (not so humble) opinion, many (most?) uses of reflection and RTTI is simply due to bad design. And that includes programs in languages that have reflection. – Some programmer dude Nov 26 '13 at 07:22
  • C++ cannot do this, but languages that can do this are written in C++. Paradox? No because what you have to do is equivalent to designing a new language with it's own notion of a class, and then implement your vehicle class in that new language. The C++ notion of a class is inadequate for your needs. – john Nov 26 '13 at 07:33
  • When do you want to know this data? At runtime (why)? During program development? – Ira Baxter Nov 26 '13 at 10:52
  • @Ira I know this data only at run time. Users can extent the application by adding content (DLL's) which serves as plug-ins to the application. Thus not at all during program development. – user3035260 Nov 26 '13 at 12:06
  • @JoachimPileborg In our case our decision to use reflection depended on the priority of our quality requirements for the application. Reflection, in our case appears to us as the correct design decision when combining it with our extensibility combined with maintainability, reduced development time and simple development process requirements. The users and our developers should preferably be able to extend our system with as little effort, complexity and errors as possible. The existing design workarounds that are commonly used seems to affect these mentioned requirements very negatively. – user3035260 Nov 26 '13 at 12:26
  • @john "C++ cannot do this" I have seen this comment a lot and, probably due to a lack of knowledge and understanding I believe this to be wrong. I know very little about compilers, but with a bit of compiler magic to add metadata to C++ objects I believe should be possible. I am hoping that someone is aware of a third party or first party solution that satisfy our needs. – user3035260 Nov 26 '13 at 13:03
  • @user3035260 A bit of compiler magic means redefining the C++ language. Did you realise that normally there is no representation of any C++ class in a compiled executable? – john Nov 26 '13 at 13:42
  • @user3035260 "compiler magic" should be a very bright warning sign regarding the maintainability of your application. You'll tie yourselves closely to some specific compiler or to using very specific class construction methods and so on. My programmer's intuition tells me, that this is a very wrong path to choose and will introduce a lot problems in the future. Be warned. You surely don't want to land on TheDailyWTF.com someday, do you? – Spook Nov 26 '13 at 13:44

5 Answers5

1

Using reflection for such task seems to be a little bit of overengineering for me. Look at the problem from the another perspective: you want to know, when someone derives from your class. This can be achieved in the following way (but not limited to):

  • Create static VehicleRepository class.
  • Prepare a following function:

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

    The task of this method is to register new vehicle type available to be constructed.

  • Require the VehicleContext in the base class' ctor of VehicleBase:

    VehicleBase::VehicleBase(VehicleContext * newContext)
    
  • Create static ctors for derived classes, which obtains the context:

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

    C++ doesn't have the static constructors, but you can emulate them in the following way: Static constructor in c++ . Also, this code can be placed in some initialization section of your application or library.

  • Use the context when instantiating new derived classes:

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

This is a simple way of making sure, that every Vehicle will register itself properly in the VehicleRepository. You also will have a centralized repository of all registered vehicles along with proper constructor functions to create them.


In general, you have to create an architecture, which follows the rule:

Instantiating derived class should be impossible, if the class didn't registered itself in some global repository prior to instantiating.

Another approach to presented above:

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!");
}

Token may be class name, GUID associated with the class or something else. Food for thought :)

Community
  • 1
  • 1
Spook
  • 25,318
  • 18
  • 90
  • 167
  • Thanks for the reply. We considered this approach. However a quick Google indicates that C++ does not have static constructors. In C# I tried this approach on a different issue. The C# static constructor is executed when the type is accessed for the first time in run time. This is an issue as it is highly probably that the type will be needed from the registry before it has been registered. E.g. The application never accessed the CarEntity, it is needed in the drop down but has never been registered. – user3035260 Nov 26 '13 at 11:44
  • You can emulate static constructors if you need to. http://stackoverflow.com/questions/5803953/static-constructor-in-c. You also can put appropriate code in some initialization section of your application or library. I wanted more to show you the direction you should follow rather than ready solution :) – Spook Nov 26 '13 at 11:51
  • Thank you. I have am concerned and was certain that static objects may be initialized to late (e.g. only when the type is used for the first time.) [link](http://stackoverflow.com/questions/5803953/static-constructor-in-c) I will have to confirm this with some test code. – user3035260 Nov 26 '13 at 12:36
  • You don't exactly have to place registering code in the static ctors (or their emulated equivalents). You can specify a method in your architecture, which is responsible for registering the classes. If they are in libraries, that's even simpler, because at some point you have to load these libraries and this is a perfect moment to ask them to register classes they provide. – Spook Nov 26 '13 at 13:40
  • Thanks for your input. This would be very much the same approach Eugene is giving in an answer below, and very much like the prototype pattern. I have raised my concerns for why we are avoiding this approach at this link: [cplusplus](http://www.cplusplus.com/forum/general/118016/) – user3035260 Nov 26 '13 at 15:20
0

This is not exactly what you want, but you can use a simple base class in C++. It does give you the ability to check for a base class. However, it does take more careful consideration and planning than in C# - as those who commented on your post suggested...

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;
}
Zak
  • 12,213
  • 21
  • 59
  • 105
0

If we want to get group of objects depending on user input string. What we can implement here is modified Factory design pattern using string input we could decide which type of vehicle objects to return.

Something like this:

#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;
}
Nik
  • 1,294
  • 10
  • 16
0

Could you provide more details on how is it planned to expand list of available types?

As other commenter said, you can't implement reflection in the way you've described in C++.

I assume all subtypes will be defined in some redistributable library (or different libraries) which will be linked dynamically with application. In this case I suppose it should be the library's responsibility to provide list of available types and some factory method to instantiate some particular subtype based on the context.

Eugene
  • 71
  • 3
  • Thanks for your feedback. "...expand list of available types" Through plug-ins, the user can extend the application by adding and removing content, Dlls to the application. Your suggestion sounds like the use of the prototype pattern. This pattern will work, but matches our applications quality requirements poorly. See my response at [link](http://www.cplusplus.com/forum/general/118016/) – user3035260 Nov 26 '13 at 11:54
  • Well, I would disagree about my suggestion resembling prototype pattern. In fact, it is factory. And you need no instance of concrete type unless you really need it. You have an interface declared: `class IVehicle { /*...*/ };` In the library you have all the subclasses you need defined, like: `class Truck : public IVehicle { /*...*/ };` And 2 functions, like: `Result GetListOfAvailableTypes (Collection& list); IVehicle CreateVehicle (const string& typeName);` In the application, wherever you need the list you call the first function, when you need concrete vehicle - you call factory. – Eugene Nov 26 '13 at 12:14
  • The problem with this 'factory' approach is that 'GetListOfAvailableTypes (Collection& list)' assumes that the application is aware of the all the possible strings of the types. There is no way for our application to know what the collection of strings should be in "Collection& list". This could be stored manually somewhere, making it work like the prototype pattern. The link in my comment above discusses our this approach. – user3035260 Nov 26 '13 at 12:49
  • No, application just defines an interface to be provided by the library. Off course it doesn't know what exactly is supplied in the collection. The library implements this function and application just uses it. Apparantly, the library's developer knows what exactly types are provided at the compile time. – Eugene Nov 26 '13 at 14:46
  • Thanks, I understand you. This method shares the same concern that I have provided in the link of my first comment. This seems to ge the general answer I am receiving. – user3035260 Nov 26 '13 at 15:17
0

I found two solutions to my problem.

The first one is trough a reflection library, Reflex. It is used by the ROOT library as well (developed by the CERN laboratory, and actively maintained.)

The second one, is what it seems @Spook and @Eugene was trying to get at. Cplusplus

Thanks for all the input.

user3035260
  • 105
  • 1
  • 9