1

Following on from this question, which asks about SFINAE it gives the example of:

template<class T>
std::string optionalToString(T* obj)
{
    if (FUNCTION_EXISTS(T->toString))
        return obj->toString();
    else
        return "toString not defined";
}

However, if an object doesn't have, for example, a toString() function, instead of this case returning "toString not defined", even if we can detect whether or not the function exists, the compiler will still throw an error that object has no member named "toString", before highlighting the pointer toString call.

I'd like to be able to do the same operations on objects that come from C++ libraries that have different naming conventions, for example:

if(R_Contains_SetPosition<TemplateObject>::Value)
{
TemplateObject->SetPosition(X,Y);
}
else if(R_Contains_setPosition<TemplateObject>::Value)
{
TemplateObject->setPosition(X,Y); //TemplateObject doesn't have a setPosition defined!
}

Which the code can already do, but the compiler throws an error on whichever statements that call functions that the object doesn't have defined.

Is there some way to get the compiler to accept code (either by re-writing it or modifying compiler flags, preferably the former) that has the ability to call a member function (which in this case won't be run anyway), even if the compiler knows said member function doesn't exist?

Clarification:

This isn't asking how to detect a function exists. I already have this ability., it's asking how can I write code that refers to functions that a object won't necessarily have without the compiler complaining about it?

The system is C++03, so experimental C++14 like solutions won't be valid here.

c1646091
  • 296
  • 1
  • 9
  • No. All code in the function is instantiated and must be valid for the type presented. You have to use sfinae and specialization to make functions that will always work with all types they are given. boost::hana has code that makes this easier and looks more like what you are suggesting, but it's syntactic sugar over sfinae. – xaxxon May 15 '16 at 18:57
  • 1
    Er... In what way is this not a complete duplicate of the question you link to yourself? Did you read the answers there before posting this question? I'm tempted to just close this, but I'll give you a chance to respond in case I'm missing something. –  May 15 '16 at 19:00
  • Because doing ->toString, even with the proposed function checkers, throws an error that the member function doesn't exist. I'm not asking 'how do I check if a function exists', I'm asking 'how do I fool the compiler into not bugging me about a function's non-existence'. – c1646091 May 15 '16 at 19:06
  • @c1646091 That is already covered in the answers there. It's covered extremely well in Johannes Schaub's answer. –  May 15 '16 at 19:08
  • Johannes Schaub's answer on analysis is largely incomplete (missing angular brackets, no return types, and no example on how to call his enable_if - which appears to violate a reserved keyword and thus has to be renamed). Although it's pseudo code, I have no idea how to make sure it works correctly. – c1646091 May 16 '16 at 12:43

1 Answers1

1

Your problem is that all branches are compiled regardless of which is taken. So all must contain valid code.

There are a few ways to get around it. Tag dispatching and SFINAE are the two most common, but neither let you do it "inline".

We can, however, use them to do it inline by writing a static_if helper, and using C++14 generic lambdas:

namespace details {
  template<class T, class F, class Else>
  decltype(auto) static_if( std::true_type, T&&t, F f, Else e ){
    return f(std::forward<T>(t));
  }
  template<class T, class F, class Else>
  decltype(auto) static_if( std::false_type, T&&t, F f, Else e ){
    return e(std::forward<T>(t));
  }
}

template<template<class...>class Test, class T, class F, class Else>
decltype(auto) static_if( T&&t, F f, Else e ){
  return details::static_if( Test<T>{}, std::forward<T>(t), std::move(f), std::move(e) );
}

This is a static_if helper.

Write can_foo<?>. Then you can use it like this:

auto r =
  static_if<can_foo>(
    t,
    [](auto&& t){return t.foo();},
    [](auto&&){return 7;}
  );

C++14: the C++11 solutions cannot in general be done inline, so you might as well tag dispatch.

live example.

Note that the return type of static_if varies based on if the template predicate evaluated on the type of the first argument is trueish or falseish.

That argument is passed to the lambda for the true/false branches. Typically you reuse the name (hiding the more global one), as the one in the lambda "magically" is the right type (will only be evaluted if the template predicate returned true on the type, basically).

Yakk - Adam Nevraumont
  • 235,777
  • 25
  • 285
  • 465
  • "std::void_t" is reported in my compiler as not being a member of std. I think the latest this compiler can go is C++11. I have no idea how to tag dispatch, nor do I know entirely how this works. I'm assuming that on compiler look-up it picks the correct branch based on the returned boolean value as a function definition lookup, but I don't fully understand how it works, nor do I know how to convert the C++14 into a C++11 or earlier compatible code. – c1646091 May 16 '16 at 12:49
  • @c16 in C++11 you cannot do it inline, and the answers to the question you link to are your only solution. Tag dispatching information can be found in a myriad of stack overflow answers. My code just does tag dispatching "inline", but it requires C++14 to do it. – Yakk - Adam Nevraumont May 16 '16 at 13:26