Ram
Ram

Reputation: 3133

Reflection in known set of classes

I am trying to iterate through an object hierarchy and the object hierarchy is composed of a known set of classes combined using composition. I would like to build an object model to show the hierarchy / composition graphically. The composition is done based on few rules but it is fluid and flexible.

Quite a few classes (25+) are available and the number of building blocks is increasing. If I search each type in every other type then we have a significantly large number of combinations possible.

I could build a large table where I search for each of the other objects for a given type and recursively build the object model but may be there is a better way and so here I am asking the experts.

Is it possible to know if a function / member variable is present on a particular type at runtime.

My sample code is shown below :

#include <iostream>

struct Generic {};

struct SimpleType {int toString(){return 0;}};

enum ETypeVal{eVal1 = 0, eVal2 = 1, eVal3 = 2};


template <typename ETypeVal val>
struct Hello
{
    int toString(){return 0;}
};

template <> struct Hello<eVal2>
{
    int toString(){return 1;}
};

template <> struct Hello<eVal3>
{   
};


template <class Type>
class TypeHasToString
{
public:
    typedef bool Yes;
    typedef short No;

    static bool const value = (sizeof(HasToString<Type>(0)) == sizeof(Yes));

private:

    template <typename T, T> struct TypeCheck;

    template <typename T> struct ToString
    {
        typedef int (T::*fptr)();
    };

    template <typename T> static Yes HasToString(TypeCheck< typename ToString<T>::fptr, &T::toString >*);
    template <typename T> static No  HasToString(...);
};

int main(int argc, char *argv[])
{
    // all this works fine
    std::cout << TypeHasToString<Generic>::value << std::endl;
    std::cout << TypeHasToString<SimpleType>::value << std::endl;
    std::cout << TypeHasToString<Hello<eVal1>>::value << std::endl;
    std::cout << TypeHasToString<Hello<eVal2>>::value << std::endl;
    std::cout << TypeHasToString<Hello<eVal3>>::value << std::endl;

    // Unable to deduce for type that are not known at compile time
    // Is it possible to remove this limitation ?
    for(int val = eVal1; val <= eVal3; val++)
    {
        std::cout << TypeHasToString< Hello< (ETypeVal)val > >::value << std::endl;
    }

    return 0;
}

Upvotes: 0

Views: 200

Answers (4)

pmr
pmr

Reputation: 59811

I've used boost::mpl to do the iteration and printing of the values. Most of this should be possible without any of those, but I heavily recommend using it. Also I've fixed some things in your code. You might also want to use BOOST_HAS_XXX instead of your homebrew solution (your SFINAE style is rather awkward).

#include <iostream>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/range_c.hpp>
#include <boost/mpl/transform.hpp>
#include <boost/mpl/for_each.hpp>


struct Generic {};

struct SimpleType {int toString(){return 0;}};

enum ETypeVal{ eVal1 = 0, eVal2 = 1, eVal3 = 2};

template <ETypeVal val>
struct Hello
{
  int toString(){return 0;}
};

template <> struct Hello<eVal2>
{
  int toString(){return 1;}
};

template <> struct Hello<eVal3>
{   
};


template <class Type>
class TypeHasToString
{
public:
  typedef bool Yes;
  typedef short No;
private:

  template <typename T, T> struct TypeCheck;

  template <typename T> struct ToString
  {
    typedef int (T::*fptr)();
  };

  template <typename T> static Yes HasToString(TypeCheck< typename ToString<T>::fptr, &T::toString >*);
  template <typename T> static No  HasToString(...);

public:
  static bool const value = (sizeof(HasToString<Type>(0)) == sizeof(Yes));
};

template<typename val>
struct make_hello { typedef Hello< ETypeVal(val::value)> type; };

struct print_seq {
  template<typename T>
  void operator()(const T&) const {
    std::cout << T::value << std::endl;
  }
};

int main()
{
  using namespace boost::mpl;
  // maybe have a last enum here
  typedef range_c<int, eVal1, eVal3 + 1>::type range; 
  // range has no clear so we need the inserter
  typedef transform<range, make_hello<_1>, back_inserter< vector0<> > >::type hellos;
  typedef transform< hellos, TypeHasToString<_1> >::type booleans;
  // namespace for clarity
  boost::mpl::for_each<booleans>( print_seq() );

  return 0;
}

Upvotes: 1

J.N.
J.N.

Reputation: 8421

You can't know at runtime if you don't at compile time. You have the code to know at compile time for one function. You could just make a macro out of it to have it for any function you want to check. (Macro disclaimer: in this case macros are good, that's how BOOST_MPL_HAS_XXX_TEMPLATE_DEF works).

Alternatively, there's boost::fusion as mentionned by dsign. But I prefer another one: boost::reflect (not actully in boost more info here). The macro syntax is easier (you don't need to mention the type in the macro) and the code is very lightweight. Then there's the feature complete boost::mirror (download here), not yet in boost, that is much more complete and even has a code generator to create the macro calls for you and a java style runtime reflection.

Upvotes: 1

dsign
dsign

Reputation: 12700

We don't have run-time reflection in C++. But we have different stuff, that most C++ programmers like better than reflection ;-).

If I understand your question, you need to build some sort of object browser, and you know all your object types. By "you know all your object types" I mean "you won't get an object as something at the other end of a pointer from a dll you didn't code".

So, maybe you can use boost::fusion? Said library is designed for iterating through aggregates, and while doing so, retrieving both data and type of each aggregate member. It is like iterators over struct members, if I have to put it in a flowery way. You can of course use it with your custom types.

Upvotes: 1

AlexTheo
AlexTheo

Reputation: 4184

I think that you need the runtime polymorphism in this case. Use interfaces instead of templates for such kind of problems. Interfaces will give the knowledge about of the methods in your object but will say nothing about the member variables. So there is no reflection available in standard c++ (the only thing that c++ provides is the type_info operator which might help you in some cases), you could try to find some extensions for your compiler which will give you the reflection opportunities.

Upvotes: 0

Related Questions