Sign in to follow this  
Amnesiac5

[C++ ] Library less function enabler (no Boost, Loki, etc.)

Recommended Posts

As part of a larger project I am trying to develop a function enabler (Dr Dobb's - Function Overloading Based on Arbitrary Properties of Types) to enable a function intended for use with a point class, the implementation and name of which are unknown. I don't want to use any library like BOOST or Loki, etc.. So far I have the following:
// constraint
template<class T1> struct must_have_XYZ {
    ~must_have_XYZ() { void(*p)() = constraints; }
private:
    static void constraints() { 
      T1& b = *new T1; 
      b.x = b.y = b.z;
    }
};

// [1]
template< typename T > 
struct supports_XYZ { 
    static const bool value = false;
};

// [2]
template<typename T>
struct supports_XYZ<must_have_XYZ<T> > {
    static const bool value = true;
};

int main() {
    bool b1 = supports_XYZ<Pt >::value;
    bool b2 = supports_XYZ<int >::value;

    return 0;
}

I am trying to use the constraint to limit the instantiation of the supports_XYZ template to [2] but, regardless of the type, [1] is always instantiated (both b1 and b2 are set to false). Since I don't know the name of the point type/class ahead of time I can't partially specialise like so:
template<>
struct supports_XYZ<Pt > { // only useful if Pt is defined
                           // won't work for Point or P3d or whatever
  static const bool value = true;
};

Hence the attempt to use a constraint. Am I barking up the wrong tree? Is this even possible? Any help with this much appreciated. A5

Share this post


Link to post
Share on other sites
As far as I know there's no totally cross-platform way to do this (if so please correct me!). Something such as the following should detect the presence of a member "x" I believe on a compliant compiler:


typedef char (&yes)[1];
typedef char (&no)[2];

template <typename T>
struct has_member_x
{
template <size_t> struct helper { };
template <typename U>
static yes test(helper<sizeof(&U::x)> *);
template <typename>
static no test(...);
static const bool value = sizeof(test<T>(0)) == sizeof(yes);
};

struct Point1 { float x; };
struct Point2 { float z; };

// has_member_x<Point1>::value should be 1
// has_member_x<Point2>::value should be 0



To make this more useful, you'd probably want to define a macro that declares the has_member_foo struct for each member you want to test for, and probably another macro to wrap the test itself.

Note that in general this is a tricky area as it depends on how the compiler treats sizeof expressions, which is by no means standard. See here for more info.

Share this post


Link to post
Share on other sites
Quote:
Original post by Amnesiac5
I am trying to use the constraint to limit the instantiation of the supports_XYZ template to [2] but, regardless of the type, [1] is always instantiated (both b1 and b2 are set to false).


The article does something clearly different from your code: template instantiation fails controllably thanks to an artificial dependence from a typedef that the "function enabler" can be made to contain or not.

Boolean constants like yours can be used to choose between the disabler and the enabler, but there are neither if statements nor boolean template parameters in your code.

Defining supports_XYZ<must_have_XYZ<T> > makes very little sense if you don't actually instantiate any must_have_XYZ<> (e.g. must_have_XYZ<Pt>).

The concern about not knowing the class name is, I'm afraid, absurd: if you don't know what classes posterity will try to use with the templates you are writing now, you cannot tell what template specializations will be needed (and you aren't even certain you'll need to mess with overloading).

In the specific case of your example, it's both hard to imagine how constraints() might be adapted to fundamentally different point representations and easy to see that it should really operate on a decently explicit point interface.
If the template function referred to accessor methods for the three coordinates you could make simple and efficient adapters for different point-like classes rather than unnecessarily assuming that point-like classes have public fields x,y and z.

Share this post


Link to post
Share on other sites
Quote:
Original post by d00fus
As far as I know there's no totally cross-platform way to do this (if so please correct me!). Something such as the following should detect the presence of a member "x" I believe on a compliant compiler:

*** Source Snippet Removed ***

Note that in general this is a tricky area as it depends on how the compiler treats sizeof expressions, which is by no means standard.

Thanks for the feedback.

Hmm, this looks like a step in the right direction. I seem to remember seeing sizeof used in similar fashion elsewhere, but blowed if I can remember where.

Any references for this?

Visual Studio spits out an "error C2070: '': illegal sizeof operand" compilation error for sizeof(test<T>(0)) if I include the line:
has_member_x<Point1 >::value;
is this the tricky area you're talking about?

has_member_x<int >::value;
does indeed evaluate to false.

A5

Share this post


Link to post
Share on other sites
In most C++03 implementations, you cannot detect whether some expressions are valid for some types at compile-time.
You need support for SFINAE for expressions to do this.

All you can detect is whether some class type provides some members using the aforementioned kind of trick.
Note it is better to use helper<U, &U::x> instead, as that is actually guaranteed to work and is much more useful since you can check the type of &U::x is what you want.

Share this post


Link to post
Share on other sites
Quote:
Original post by Amnesiac5
Visual Studio spits out an "error C2070: '': illegal sizeof operand" compilation error for sizeof(test<T>(0)) if I include the line:
has_member_x<Point1 >::value;
is this the tricky area you're talking about?


Yes, in this case I think VC++ doesn't support the technique. GCC and Comeau both do however. From memory in VC++ there is a compiler extension to perform the same job, __if_exists or something similar. If you are desperate to go down this route, you could conditionally compile the correct version for the compiler you're using.

Share this post


Link to post
Share on other sites
Quote:
Original post by LorenzoGatti
Quote:
Original post by Amnesiac5
I am trying to use the constraint to limit the instantiation of the supports_XYZ template to [2] but, regardless of the type, [1] is always instantiated (both b1 and b2 are set to false).

The article does something clearly different from your code: template instantiation fails controllably thanks to an artificial dependence from a typedef that the "function enabler" can be made to contain or not.

Boolean constants like yours can be used to choose between the disabler and the enabler, but there are neither if statements nor boolean template parameters in your code.

Defining supports_XYZ<must_have_XYZ<T> > makes very little sense if you don't actually instantiate any must_have_XYZ<> (e.g. must_have_XYZ<Pt>).

The concern about not knowing the class name is, I'm afraid, absurd: if you don't know what classes posterity will try to use with the templates you are writing now, you cannot tell what template specializations will be needed (and you aren't even certain you'll need to mess with overloading).

In the specific case of your example, it's both hard to imagine how constraints() might be adapted to fundamentally different point representations and easy to see that it should really operate on a decently explicit point interface.


I'm not too clear what you're saying so please bear with me if I've misinterpreted you.

I haven't included any if's since I haven't reached that point in development yet (I believe they're implicit anyway) I'm working towards that point but first need to define a traits class or something like a traits class which will evaluate to true or false dependent on the properites of the passed parameter. In this case, I want members x, y and z (hence the supports_XYZ trait). If they're not present an alternative template would be instantiated or a compiler error would be caused. I'm concerned about the classes properties not it's name.

The constraint is my (clearly flawed) attempt at something like a traits class.

It was my understanding that since must_have_XYZ<T> is inside template<typename T> struct supports_XYZ it would pick up T from there. Clearly, I'm wrong there, too.
Quote:
If the template function referred to accessor methods for the three coordinates you could make simple and efficient adapters for different point-like classes rather than unnecessarily assuming that point-like classes have public fields x,y and z.

True, but I would've thought that public fields would be just as likely as accessors.

A5

Share this post


Link to post
Share on other sites
Quote:
Original post by loufoque
Note it is better to use helper<U, &U::x> instead, as that is actually guaranteed to work and is much more useful since you can check the type of &U::x is what you want.


You're right that it's probably better to implement it using pointer-to-member template arguments, however I got the impression that the OP didn't know (or didn't want to specify) the type of the member in advance, in which case I don't think you can use this technique.

OP: I should also emphasise the point others have raised, which is that is this check really necessary for your use? If the only purpose is to prevent instantiation of a template if the type does not have x/y/z members (or getX/getY/getZ accessors), the compiler would do this for you by giving an error when you reference them. What exactly do you want to do with the bool value you get?

Share this post


Link to post
Share on other sites
Quote:
Original post by d00fusOP: I should also emphasise the point others have raised, which is that is this check really necessary for your use? If the only purpose is to prevent instantiation of a template if the type does not have x/y/z members (or getX/getY/getZ accessors), the compiler would do this for you by giving an error when you reference them. What exactly do you want to do with the bool value you get?
Whoa, I've been working on this for so long and moved so far from where I started I've almost forgotten...

I have several functions which read data from an input stream into a data structure, the data structure may be a sequence container (eg vector<int> or list<float>) or a point. So I want the code to select the correct functions to implement at compile time.

Share this post


Link to post
Share on other sites
Quote:
Original post by d00fus
As far as I know there's no totally cross-platform way to do this (if so please correct me!). Something such as the following should detect the presence of a member "x" I believe on a compliant compiler:

*** Source Snippet Removed ***

To make this more useful, you'd probably want to define a macro that declares the has_member_foo struct for each member you want to test for, and probably another macro to wrap the test itself.

Note that in general this is a tricky area as it depends on how the compiler treats sizeof expressions, which is by no means standard. See here for more info.

Looks like this method won't work with VS 2005 according to this article. I'll try it with 2008 when I get home tonight.

[edit] fails in VS 2008, too, dang! Workaround, anyone?

A5

[Edited by - Amnesiac5 on June 9, 2009 4:23:54 PM]

Share this post


Link to post
Share on other sites
Quote:
Original post by d00fus
If you're VC++ based, maybe try __if_exists?
That's certainly a possibility, but Paul Sinnett has provided me with a solution:

template <typename T>
struct has_member_x {
template <bool> struct helper {}; // change struct_t to bool
template <typename U>
static yes test(helper<&U::x> *); // remove sizeof
template <typename>
static no test(...);
static const bool value = sizeof(test<T>(0)) == sizeof(yes);
};

Now I just need to spend some time trying to figure out how the blazes it works !

Thanks to all who contributed.

A5

Share this post


Link to post
Share on other sites
Quote:
Original post by Amnesiac5
That's certainly a possibility, but Paul Sinnett has provided me with a solution:
*** Source Snippet Removed ***


Hmm that's interesting. From my reading of the standard, it appears that this isn't legal according to 14.3.2p5:

"The following conversions are performed on each expression used as a non-type template-argument. If a non-type template-argument cannot be converted to the type of the corresponding template-parameter then the program is ill-formed.
...
# For a non-type template-parameter of type pointer to data member, qualification conversions (4.4) are applied."

That suggests that the pointer-to-data-member nontype argument (&U::x) should not implicitly convert to bool, as qualification conversion only basically allows cv-qualification. Is there someone more language-lawyerly than me who'd care to comment? :)

Edit: I should note that all of VC++, Comeau and GCC seem to accept it, which is why I'm not sure my reading of the standard is correct!

Edit2: Actually, did you check that the posted code actually works? :) It does compile, but that seems to be because the compiler always selects the test(...) version, as per my comments above. That means of course that has_member_x<T>::value is always false however, regardless of T.

[Edited by - d00fus on June 11, 2009 7:58:40 AM]

Share this post


Link to post
Share on other sites
Quote:
Now I just need to spend some time trying to figure out how the blazes it works !

It's just SFINAE, with an ellipsis overload that is always the worst match, and a sizeof-based trick to tell which overload is being called.

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