2

I want to check a provided template class for the existence of some methods at compile-time. Made-up example code:

template <class AudioSystem>
class Car {
public: 
  Car() {/*nothing related to AudioSystem*/}
  void getsCalledLater(AudioSystem* a) {/*...*/} 
  void Verify() { 
    // check if class AudioSystem has function double hasFeature(int, double)
    // check if class AudioSystem has function double getCreationDate() 
  }
  ~Car() {
    Verify();
  }
};

I do not have an AudioSystem object when the constructor gets called, so I can't just do a test call for the methods. Also: I cannot assume that the default ctor of AudioSystem is available.

I found this question already here on SO, which pointed to

http://www.gotw.ca/gotw/071.htm

but I do not understand this innocent one-line solution:

// in Validation method:
T* (T::*test)() const = T::Clone; // checks for existence of T* T::Clone() const

Any help is appreciated.

(If it is not possible without having access to the default ctor, I might be able to drop that requirement.)

mstrkrft
  • 53
  • 5
  • 2
    Do you just want to *ensure* that the member function exists, or do you want to switch your template specializations based on *whether* the function exists? – Kerrek SB Nov 17 '14 at 23:17
  • This could be done at compile-time, too. – NaCl Nov 17 '14 at 23:18
  • @KerrekSB: Just ensure (i.e. stop compiling if nonexistant), but the other case would be interesting too! – mstrkrft Nov 17 '14 at 23:46
  • 1
    @mstrkrft: There are well-known SFINAE techniques for the general case. Probably not worth repeating them here, there should be several existing posts about that on this site. – Kerrek SB Nov 17 '14 at 23:48

1 Answers1

1

The line

T* (T::*test)() const = T::Clone;

declares test as a pointer to const-member-function of T, the latter taking no parameters and returning a pointer to T. Then it initializes to point to T::Clone member function. Now, if T::Clone has a different signature than (void)->T* or does not exist, then you'll get an error.

Very smart.

Let's look at this example:

template<typename T>
class Check // checks for the existence of `T* T::Clone() const`
{
public:
  ~Check()
  {
      T* (T::*test)() const = &T::Clone;
      // test; // don't think you need this line  
  }
};

class Foo
{
public:
    Foo* Clone() const{}; // try changing the signature, remove `const` for example
};

int main()
{
    Check<Foo> testFoo; // ok
}

Now try removing the const from the signature of Foo::Clone(), or make it return an int, and you'll get a compile time error, as the pointer declared in Check<T> is not compatible anymore to the right type of function. Hope this makes sense.

Note that this kind of verification is done at compile time, so you cannot have a bool function that returns true or false (like you are trying to do now), as this implies run-time decision. So you HAVE TO use this kind of trick, and IF the function exists, then the program will compile, if not, you will get a compile-time error.

So, in your case, to test for example for the existence of double AudioSystem::hasFeature(int, double), you need to declare

double (AudioSystem::*test)(int, double) = AudioSystem::hasFeature;
vsoftco
  • 55,410
  • 12
  • 139
  • 252
  • Thanks! For my example, a function `double test(int, double)`, the solution is `double (T::*test)(int, double) = &T::test;`. – mstrkrft Nov 17 '14 at 23:58
  • Yes, forgot to post the solution to your problem :) Will update the answer so it appears. – vsoftco Nov 17 '14 at 23:59
  • Is there any chance to add a message to the compiler output in this case (like via static_assert)? – mstrkrft Nov 17 '14 at 23:59
  • @mstrkrft that's a good question, probably some meta-template guru can come up with a solution, but right now I don't know how to do it. You may take a look here, http://stackoverflow.com/questions/8911897/detecting-a-function-in-c-at-compile-time – vsoftco Nov 18 '14 at 00:06