Sign in to follow this  
Stoic

Trying to extract the type of a class member from the name

Recommended Posts

Hi all - Warning, silly Template Metaprogramming to follow... I'm doing some crazy macro-driven custom RTTI stuff, and I'm trying to extract the type of a class's data member from the class name and the member name. I want to use the extracted type to create a global instance of a templated class, so it all has to happen at compile time. My ultimate goal is to do something like this -

#define RTTI_MEMVAR_REG( MemberOfClassName, memberName )     namespace {     MemVarInstance<MemberOfClassType, MemberType> VarInst_##memberName(#memberName);     }


//Then it could be used like this ....

//TestClass.h
class TestClass { public:float f; }; //first problem is that this has to be public

//in TestClass.cpp
RTTI_MEMVAR_REG( TestClass, f );



It's proving a lot more difficult than I expected. I had really high hopes for this approach -
template <class T> class ExtractMemberType;

template < class MemberOf, class MemberType >
class ExtractMemberType< MemberType MemberOf::* >
{
	typedef MemberType result;
};



The above classes compile, but unfortunately, it doesn't seem to solve my problem.

void main()
{
    printf( "%u\n", sizeof( ExtractMemberType<&TestClass::f>::result ) );

    //Won't compile - "expected Type as template argument to ExtractMemberType"

}


The problem I'm running into is that I can't declare the type of the member data pointer without knowing the type of the member, which I'm having the darnedest time coming up with (at least in a fashion that could be used as a template parameter). Here's another idea, but I don't know how to get it into the template parameter I need...
&( ((ClassName*)(NULL))->(MemberName) )  

//This expression should have the correct type, but I can't use it as a template
//  parameter. I could pass the expression into a (runtime) function, but unfortunately that doesn't help me create the variable I need.  Hmm.. maybe a static local variable to the function instead of the global....


Anyone have any other ideas? Thanks in advance for any help!

Share this post


Link to post
Share on other sites
Quote:
Original post by Stoic
Hi all -

Warning, silly Template Metaprogramming to follow...

I'm doing some crazy macro-driven custom RTTI stuff, and I'm trying to extract the type of a class's data member from the class name and the member name. I want to use the extracted type to create a global instance of a templated class, so it all has to happen at compile time.


I fail to see how that could be useful, but let me suggest another approach: provide a typedef for the member, and a macro to set up the typedef.


#define TYPEDEFFED_MEMBER(a, t, x) public: typedef t x##_type; a: x##_type x

class TestClass {
TYPEDEFFED_MEMBER(private, float, f);
};

TestClass::f_type value;

Share this post


Link to post
Share on other sites
It can be done but it depends on exactly what MemVarInstance does, if you could post it it would make it a lot easier. Basically what you need to do is pass it to a function to deduce the type, once you do that you can create a static instance of the member from within that function [or a different function] and ensure that each usage of the macro results in that function being instantiated with different template parameters. Then to retrieve it you can call the function again with the correct template parameters which returns a reference to the stored value.


template<typename T, typename MemT>
struct MemberForwarder
{
template<MemT T::*>
MemVarInstance<T, MemT>& forward(const char* mem_name) const
{
static MemVarInstance<T, MemT> mem_var_inst(mem_name);
return mem_var_inst;
}
}

template<typename T, typename MemT>
MemberForwarder<T, MemT> deduce(MemT T::*)
{
return MemberForwarder<T, MemT>();
}

#define Reg(T, Mem) \
namespace { \
struct Create##Mem { Create##Mem() { \
deduce(&T::Mem).forward<&T::Mem>(#Mem); } }; \
Create##Mem create##Mem; } \
/********************************************************/



Something along those lines should work, although, you will probably need to change it depending on what MemVarInstance does.

Share this post


Link to post
Share on other sites
@Zhalman - Hmm... that works. I was hoping to avoid needing to use macros for every member variable, but there are some advantages to that. I like the idea of being able to just do a mapping of names to types like that, so I'll keep a note. In terms of usefulness, I'll explain below.

@Julian90 - That's super awesome... I think that's pretty close to exactly what I need.


Filling in the extra information - my templated MemVarInstace classes all derive from a common base class that serves as an interface to the MemVarInstance, plus it allows them to all autolist by type. I have an AutoLister Mixin class which the base class derives from which adds the MemVarInstance to a list in its constructor. I also have similar macros for registering full on classes, which the MemVars may wind up accessing later. The ultimate goal for all this is to use it for auto-generating serialization code, network replication, and binding to scripting languages.

It looks kind of like this -

class IMemVar
{
public:
virtual size_t SizeOf()=0;
virtual size_t OffsetOf()=0;

//this breaks the "pure virtualness", but leads to less templated code
const char* name;
};

//This class creates a Type for the Memvars to AutoList to
// - wish I could get rid of the MI, but it isn't really harmful here
template < class MemberOf >
class BaseMemVar : public IMemVar, public AutoLister< BaseMemVar< MemberOf > >
{
public:
BaseMemVar() {}
};

//This is the same as the "MemVarInstance" thing I was talking about above
template < class MemberOf, class MemberType >
class MemVarImpl : public BaseMemVar< MemberOf >
{
public:
MemVarImpl(const char* _name, size_t Offset):name(_name), offset(Offset) {}

size_t SizeOf() { return sizeof(MemberType); }

//offsetof is used as part of the registration macro
size_t OffsetOf() { return offset; }

size_t offset;
};





I also have some external functions which search through the (auto)lists and can generate pointers, etc, based on string names, hence the serialization, scripting, etc... as an optimization later on, I was planning on generating TypeID's after startup by iterating through the lists (after sorting to get around uncertainties based on compiler, etc... )

The idea is that other people will simply create client classes, then expose them to this system by using the Macros in the .cpp file. This will give them all the functionality of the autosystem. I'm also trying to leave some hooks for people to overload the autogeneration system (probably through explicit template specialization of their class) to write their own optimized or specialized versions of serialization or whatever.


Thanks a bunch for the suggestion! I'll have to try it out after work tonight!

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this