Reputation: 3133
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
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
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
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
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