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

Started by
12 comments, last by loufoque 14 years, 10 months ago
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
Constipation is the thief of time.Diaorrhea waits for no man.
Advertisement
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.
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.

Omae Wa Mou Shindeiru

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
Constipation is the thief of time.Diaorrhea waits for no man.
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.
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.
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
Constipation is the thief of time.Diaorrhea waits for no man.
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?
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.
Constipation is the thief of time.Diaorrhea waits for no man.
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]
Constipation is the thief of time.Diaorrhea waits for no man.

This topic is closed to new replies.

Advertisement