How to save pointer to member in compile time?

ぃ、小莉子 提交于 2019-12-03 15:03:52

Why using a template?

#include <cstdio>

struct Foo {
    int a;
    int b;
} foo = {2, 3};

int const (Foo::*mp) = &Foo::b;

int
main() {
    printf("%d\n", foo.*mp);
    return 0;
}

The following compiles mp to this on gcc-4.4.1 (I don't have access to MSVC at the moment):

.globl mp
        .align 4
        .type   mp, @object
        .size   mp, 4
mp:
        .long   4

It is just an offset to the member, which looks pretty compile-time to me.

With template, you need to specify the definition outside of the class:

#include <cstdio>

struct Foo {
   int m;
   int r;
} foo = {2, 3};

template<int Foo::*Mem>
struct B {
   static int Foo::* const mp;
};

template<int Foo::*Mem>
int Foo::* const B<Mem>::mp = Mem;

int main() {
    typedef B<&Foo::m> Bm;
    typedef B<&Foo::r> Br;
    printf("%d, %d\n", foo.*(Bm::mp), foo.*(Br::mp));
}

Which compiles to:

g++ -O2 -S -o- b.cc | c++filt

...

        .weak   B<&(Foo::r)>::mp
        .section        .rodata._ZN1BIXadL_ZN3Foo1rEEEE2mpE,"aG",@progbits,B<&(Foo::r)>::mp,comdat
        .align 4
        .type   B<&(Foo::r)>::mp, @object
        .size   B<&(Foo::r)>::mp, 4
B<&(Foo::r)>::mp:
        .long   4
        .weak   B<&(Foo::m)>::mp
        .section        .rodata._ZN1BIXadL_ZN3Foo1mEEEE2mpE,"aG",@progbits,B<&(Foo::m)>::mp,comdat
        .align 4
        .type   B<&(Foo::m)>::mp, @object
        .size   B<&(Foo::m)>::mp, 4
B<&(Foo::m)>::mp:
        .zero   4

However this all smacks of standard library features reimplementation (see std::tr1::mem_fn).

It would be nice to have more elaborate explanation of why 'compile-time is important' (helps suggesting alternatives). But to my notion everything you need to be done compile time with pointer-to-member, you actually can do. My variant is Thomas's suggestion blended with some C++ philosophy of sort. First lets define:

template <typename T, T v>
struct val
{};

this struct template can effectively serve as compile time value, and you don't need "static value = v;", to use it either at compile or run time. Consider:

template <int n>
struct Foo
{
  //something dependent on n
};

and

template <typename T>
struct Bar;

template <int n>
struct Bar <val <int, n> >
{
  //something dependent of n
};

Foo and Bar are functionally equivalent, every template meta-kadabra which can be done with Foo can also be done with Bar (just pass val instead of n). The same vay you can pack pointer to member into val<>:

val <typeof (&My::a), &My::a>

these compile time values now can be stored in type lists (like boost::mpl::something), compared, transformed, etc., everything compile time. And when you will finally want to use them as pointer-to-member at run time, just define one function template:

template <typename T, T value>
T
extract (val <T, value>)
{
   return value;
}

and use it:

typedef val <typeof (A::i), A::i> a_i;

A a;
std::cout << (a .* extract (a_i ()));

P.S.: there are some clumsy constructs about this solution, but it is all for sake of simplicity and explanation. For example rather ugly (a .* extract (a_i ())) may be simplified by wrapping it into something more pointer-to-member specific:

template <typename M, typename C>
typename mem_type <M>::value &
mem_apply (C &c)
{
   M m;
   return c .* extract (m);
}

where mem_type is class template which extracts type of member referred by M. Then usage would be:

std::cout << mem_apply <a_i> (a);

You can't.

But you can use a functionoid instead. This can be a compile-time solution. And because the compiler can inline things, it's possibly even faster than a pointer to a member function. Example:

struct Foo {
   int m; 
   int r;
};

struct FooM {
   static int call(Foo const &foo) const { return foo.m; }
}

struct FooR {
   static int call(Foo const &foo) const { return foo.r; }
}

template<typename FooFun>
struct B {
   typedef FooFun foo_fun;
   int call_foo_fun(Foo const &foo) { return foo_fun::call(foo); }
};

// Example of using
int main() {
    typedef B<FooM> Bm;
    typedef B<FooR> Br;
    Foo foo;
    std::cout << Bm.call_foo_fun(foo);
}

Untested, but you get the idea.

You can't initialize a static member inside the definition of a struct. It needs to be declared outside like this (which is probably not what you intended with the template, but anyway):

struct Foo {
   int m; 
   int r;
};

template<int Foo::*ptr_to_member>
struct B {
   static int Foo::* const saved_ptr_to_member; 
};

int Foo::* const B<&Foo::m>::saved_ptr_to_member = &Foo::m; 
int Foo::* const B<&Foo::r>::saved_ptr_to_member = &Foo::r; 

// Example of using
int main() {
    typedef B<&Foo::m> Bm;
    typedef B<&Foo::r> Br;
    Foo foo;
    std::cout << (foo.*(Bm::saved_ptr_to_member));
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!