Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Template metafunction returning pointer-to-member

7 views
Skip to first unread message

Marsh Ray

unread,
Sep 24, 2008, 10:58:59 AM9/24/08
to
Does anyone have a technique for a template metafunction to return a
pointer-to-member?

Thanks,

- Marsh

struct sample
{
float member_one;
};

template <typename T, int N> struct choose_p2mem { };

template <> struct choose_p2mem<sample, 1>
{
// Error: a member of type "int sample::*const" cannot have an in-
class initializer
//static int sample::* const ptr2mem = &sample::member_one;

static float sample::* const ptr2mem;
};

// Error: "float sample::*const choose_p2mem<sample, 1>::ptr2mem" is
not an entity that can be explicitly specialized
template <>
float sample::* const choose_p2mem<sample, 1>::ptr2mem =
&sample::member_one;

void f()
{
BOOST_STATIC_ASSERT(&sample::member_one == choose_p2mem<sample,
1>::ptr2mem);
}

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Yechezkel Mett

unread,
Sep 25, 2008, 5:31:02 PM9/25/08
to
On Sep 24, 5:58 pm, Marsh Ray <marsh...@gmail.com> wrote:
> struct sample
> {
> float member_one;
> };
>
> template <typename T, int N> struct choose_p2mem { };
>
> template <> struct choose_p2mem<sample, 1>
> {
> // Error: a member of type "int sample::*const" cannot have an in-
> class initializer
> //static int sample::* const ptr2mem = &sample::member_one;
>
> static float sample::* const ptr2mem;
>
> };
>
> // Error: "float sample::*const choose_p2mem<sample, 1>::ptr2mem" is
> not an entity that can be explicitly specialized
> template <>
> float sample::* const choose_p2mem<sample, 1>::ptr2mem =
> &sample::member_one;

It's nothing to do with the ptr-to-member; the same thing would happen
with any type. Try the following:

float sample::* const choose_p2mem<sample, 1>::ptr2mem =
&sample::member_one;

(without the "template <>").

The point is that "choose_p2mem<sample, 1>" is not a template; it's a
full specialization, which is just a plain class. Note that this
definition will need to go in a cpp file, not a header file, like all
class member definitions.

Yechezkel Mett

Marsh Ray

unread,
Sep 25, 2008, 8:50:44 PM9/25/08
to
On Sep 25, 4:31 pm, Yechezkel Mett <ymett.on.use...@gmail.com> wrote:
> It's nothing to do with the ptr-to-member; the same thing would happen
> with any type. Try the following:
>
> float sample::* const choose_p2mem<sample, 1>::ptr2mem =
> &sample::member_one;

Thanks, that was part of my compile error, but I'm not much closer to
my goal. It seems I can provide types and integral constant
expressions
as compile-time constants from my template, but not pointer-to-
members.

#include <boost/mpl/bool.hpp>
#include <boost/mpl/assert.hpp>

struct sample { float member_one; };

template <
typename T,
int N,
float sample::* P2M // can take a pointer-to-member just fine
>
struct republish_my_own_template_args_as_compile_time_const
{
typedef T t; // OK
static const int n = N; // OK
// static float sample::* const p2m = P2M; // No luck.
// "a member of type "float sample::*const" cannot have an in-class
initializer"
};

So I made a half-successful attempt to trick the compiler into
allowing it:

// Simple metapredicate for testing. Note that we can specialize
on
// a pointer-to-member just fine.

template <float sample::* P2M>
struct is_sample_member_one : boost::mpl::false_ { };

template <> struct is_sample_member_one<&sample::member_one> :
boost::mpl::true_ { };

BOOST_MPL_ASSERT((is_sample_member_one<&sample::member_one>)); //
OK
BOOST_MPL_ASSERT_NOT((is_sample_member_one<0>)); // OK

// Template to specialize and yield pointer-to-member.

template <typename T, int N, typename V = void> struct
choose_p2mem;

// This actually compiles. It's not a full specialization.
template <typename V> struct choose_p2mem<sample, 1, V>
{


static float sample::* const ptr2mem;
};

template <typename V>
float sample::* const choose_p2mem<sample, 1, V>::ptr2mem =
&sample::member_one;

int main()
{
// Works at run-time.
if (choose_p2mem<sample, 1>::ptr2mem != &sample::member_one)
throw -1;

// Breaks at compile-time.
//BOOST_MPL_ASSERT((is_sample_member_one<choose_p2mem<sample,
1>::ptr2mem>));

return 0;
}

But (as usual) the compiler gets the last laugh. I can make it produce
the correct pointer-to-member at run-time, but it refuses to use its
knowledge at compile-time.

Any other ideas?

So it seems I can take ptr-to-mems as non type template arguments,
and even specialize on them, but cannot publish them from structs
as compile-time constants. I don't see an obvious reason for this
limitation. Perhaps it's just one of those little corners below
the horizon of the vendors and standards group.

Will I be able to do this in C++ 0x?

- Marsh

Chris Fairles

unread,
Sep 28, 2008, 10:58:59 AM9/28/08
to
> So it seems I can take ptr-to-mems as non type template arguments,
> and even specialize on them, but cannot publish them from structs
> as compile-time constants.

What about something like:

struct A { float f; };

template<float A::* P2M>
struct B
{
static float A::* const p2m;
};

template<float A::* P2M> float A::* const B<P2M>::p2m = P2M;

template<float A::* P2M>
struct C
{
};

int main()
{
A a;
a.f = 4.2;

//prints 4.2
std::cout << a.*B<&A::f>::p2m << std::endl;

//used as compile-time constant
C<B<&A::f>::p2m> c;
}

or am I misunderstanding the issue?

Chris

Yechezkel Mett

unread,
Oct 2, 2008, 7:46:31 PM10/2/08
to
On Sep 26, 3:50 am, Marsh Ray <marsh...@gmail.com> wrote:
> On Sep 25, 4:31 pm, Yechezkel Mett <ymett.on.use...@gmail.com> wrote:
>
> > It's nothing to do with the ptr-to-member; the same thing would happen
> > with any type. Try the following:
>
> > float sample::* const choose_p2mem<sample, 1>::ptr2mem =
> > &sample::member_one;
>
> Thanks, that was part of my compile error, but I'm not much closer to
> my goal. It seems I can provide types and integral constant
> expressions
> as compile-time constants from my template, but not pointer-to-
> members.
...

>
> So it seems I can take ptr-to-mems as non type template arguments,
> and even specialize on them, but cannot publish them from structs
> as compile-time constants. I don't see an obvious reason for this
> limitation. Perhaps it's just one of those little corners below
> the horizon of the vendors and standards group.
>
> Will I be able to do this in C++ 0x?

I would have expected some use of constexpr to allow this, but looking
at the draft C++0x standard (N2588) I see the following:
"""
14.3.2 Template non-type arguments [temp.arg.nontype]
1 A template-argument for a non-type, non-template template-parameter
shall be one of:
� an integral constant expression; or
� the name of a non-type template-parameter; or
� the address of an object or function with external linkage,
including function templates and function template-ids but excluding
non-static class members, expressed as & id-expression where the & is
optional if the name refers to a function or array, or if the
corresponding template-parameter is a reference; or
� a constant expression that evaluates to a null pointer value (4.10);
or
� a constant expression that evaluates to a null member pointer value
(4.11); or
� a pointer to member expressed as described in 5.3.1.
"""

Note that the fourth and fifth items allow constant expressions for
pointers and pointer-to-members only if they evaluate to null. The
third and sixth items allow pointers and pointers to members if they
are of the form &object or &class::member, but no other expressions.
So it seems that there is no way to do it.

> Any other ideas?

Depending on what you are trying to do you might be able to invert the
logic. For your original example, you can do something like this:

struct sample
{
float member_one;
};

template<class T, int N, template<float T::*> class F>
struct choose_and_apply_p2m;

template<template<float sample::*> class F>
struct choose_and_apply_p2m<sample, 1, F>
{
typedef F<&sample::member_one> type;
};

template<float sample::* p>
struct is_member_one
{
static const bool value = false;
};
template<>
struct is_member_one<&sample::member_one>
{
static const bool value = true;
};

void f()
{
BOOST_STATIC_ASSERT((choose_and_apply_p2m<sample, 1,
is_member_one>::type::value));
}

Yechezkel Mett

0 new messages