views:

132

answers:

3

Basically, I have lots of differently typed structs like this:

typedef struct
{
    char memberA;
    int memberB;
    ...
} tStructA;

Is it possible to use a template to get/extract an arbitrary member from the struct? In pseudocode, I'm looking for something like this:

/*This is pseudocode!*/
template <typename STRUCT_TYPE, typename MEMBER_TYPE, membername NAME>
class cMemberExtractor
{
    public:
        MEMBER_TYPE
        extract(const STRUCT_TYPE* pStruct) const
        {
             return pStruct->NAME;
        }
};    

The idea behind is to use the template like this:

/*somewhere*/
void
producer()
{
    //produce update
    tStructA* pUpdate=new tStructA;
    ...
    //send update to receivers
    emit(pUpdate);
}


/*elsewhere*/
void
consumer(const tStructA* pUpdate)
{
    //extract data
    int data=cMemberExtractor<tStructA,int,memberB>().extract(pUpdate);
    //process data
    ...
}

Thanks for your help!

A: 

You need help from macros.

#include <cstddef>

template <typename StructType, typename MemberType, size_t member_offset>
struct cMemberExtractor {
        MemberType extract(const StructType* pStruct) const {
             const char* member_loc = reinterpret_cast<const char*>(pStruct) + member_offset;
             return *(reinterpret_cast<const MemberType*>(member_loc));
        }
};

#define M_MEMBER_EXTRACTOR(STRU, MEMTYPE, MEMNAME) \
 (cMemberExtractor<STRU,MEMTYPE,offsetof(STRU,MEMNAME)>())
...

int data = M_MEMBER_EXTRACTOR(tStructA,int,memberB).extract(pUpdate);

If your compiler supports the typeof operator, the MEMTYPE argument can be eliminated to help type safety.

#define M_MEMBER_EXTRACTOR(STRU, MEMNAME) \
 (cMemberExtractor<STRU,typeof(((STRU*)0)->MEMNAME),offsetof(STRU,MEMNAME)>())
...

int data = M_MEMBER_EXTRACTOR(tStructA,memberB).extract(pUpdate);
KennyTM
@KennyTM: Thx for your post, but I'd like to avoid #defines, whenever possible...
Axel
+4  A: 

You can do that not with names but with member pointers:

template <typename C, typename M>
struct updater_t {
   typedef M C::*member_ptr_t;

   updater_t( member_ptr_t ptr, M const & new_value )
      : new_value( new_value ), ptr(ptr)
   {}
   updater_t( member_ptr_t ptr, C & original )
      : new_value( original.*ptr ), ptr(ptr)
   {}
   void operator()( C & obj ) {
      obj.*ptr = new_value;
   }
   M new_value;
   member_ptr_t ptr;
};
struct test {
   int value;
};
int main() {
   updater_t<test,int> update( &test::value, 10 );
   test object;
   update( object );

   test object2;
   updater_t<test,int> update_copy( &test::value, object );
   update_copy( object2 );
}

Edit: Moving the member pointer to a template argument as suggested by litb:

template <typename C, typename M, M C::* Ptr>
struct updater_t {
   updater_t( M const & new_value ) : new_value( new_value ) {}
   updater_t( member_ptr_t ptr, C & original ) : new_value( original.*Ptr ) {}
   void operator()( C & obj ) {
      obj.*ptr = new_value;
   }
   M new_value;
};
int main() {
   updater_t<test,int, &test::value> update( 10 );
   test object;
   update( object );
}
David Rodríguez - dribeas
Yeah, that's exactly what I want (took me quite a time because I havn't seen member pointers so far)...thank you veeery much!
Axel
Note that the main usage of this type of object is not actually the syntax (which will make some IDEs choke on autocompletion), but rather that it can be used as a brick for an undo stack or transactional changes.
David Rodríguez - dribeas
It's actually possible to have it possibly more performant and nearer to the desired usage by putting the member pointer into the template like the question shows: `template <typename C, typename M, M C::*p> class updater_t { .. };`.
Johannes Schaub - litb
Right. That would reduce the memory overhead in a pointer per updater, and change the creation syntax. I thought of it, and I don't quite know why I discarded that option.
David Rodríguez - dribeas
+1  A: 

This works for me:

#include <iostream>

struct Foo {
    int member;
    Foo() : member() {}
};

template< typename T, typename C >
T& extract(C& obj, T C::* member)
{
    return (obj.*member);
}

int main()
{
    Foo foo;
    std::cout << foo.member << '\n';
    extract(foo, &Foo::member) = 42;
    std::cout << foo.member << '\n';
    return 0;
}

extract(Object, &Class::Member) returns a reference to Member in Object. Is that what you wanted?

sbi
Yes, that's exactly what I wanted! Thank you! (Sry, David has been faster...)
Axel