8

As a follow-up to my previous question, I am trying to detect the existence of a template function that requires explicit specialization.

My current working code detects non-template functions (thanks to DyP's help), provided they take at least one parameter so that dependent name lookup can be used:

// switch to 0 to test the other case
#define ENABLE_FOO_BAR 1

namespace foo {
  #if ENABLE_FOO_BAR
    int bar(int);
  #endif
}

namespace feature_test {
  namespace detail {
    using namespace foo;
    template<typename T> decltype(bar(std::declval<T>())) test(int);
    template<typename> void test(...);
  }
  static constexpr bool has_foo_bar = std::is_same<decltype(detail::test<int>(0)), int>::value;
  static_assert(has_foo_bar == ENABLE_FOO_BAR, "something went wrong");
}

(the ENABLE_FOO_BAR macro is just for testing purpose, in my real code I don't have such a macro available otherwise I wouldn't be using SFINAE)

This also works perfectly with template functions when their template arguments can automatically be deduced by the compiler:

namespace foo {
  #if ENABLE_FOO_BAR
    template<typename T> int bar(T);
  #endif
}

However when I try to detect a template function that requires explicit specialization, the static_assert kicks in when foo::bar() exists:

namespace foo {
  #if ENABLE_FOO_BAR
    template<typename T, typename U> T bar(U);
  #endif
}

//...
// error: static assertion failed: something went wrong

Obviously the compiler can't deduce the template arguments of bar() so the detection fails. I tried to fix it by explicitly specializing the call:

template<typename T> decltype(bar<int, T>(std::declval<T>())) test(int);
//      explicit specialization  ^^^^^^^^

This works fine when foo::bar() exists (the function is correctly detected) but now all hell breaks loose when foo::bar() doesn't exist:

error: ‘bar’ was not declared in this scope
     template<typename T> decltype(bar<int, T>(std::declval<T>())) test(int);
                                   ^
error: expected primary-expression before ‘int’
     template<typename T> decltype(bar<int, T>(std::declval<T>())) test(int);
                                       ^
// lots of meaningless errors that derive from the first two

It seems my attempt at explicit specialization failed because the compiler doesn't know that bar is a template.

I'll spare you everything I tried to fix this and get straight to the point: how can I detect the existence of a function such as template<typename T, typename U> T bar(U); that requires explicit specialization in order to be instantiated?

Community
  • 1
  • 1
syam
  • 14,701
  • 3
  • 41
  • 65
  • I'm not really sure if this helps for your problem, but the way I know to check for interfaces is trying to instantiate function pointers for certain expected functions (static or members), and fail compilation there first. I can't tell if it works with namespace global functions. My 1st approaches leaned on the proposals made in this [document](http://www.oonumerics.org/tmpw00/mcnamara.pdf), I think some of the mechanisms used there are replaceable using some STL SFINAE asserters, but the principles still apply. – πάντα ῥεῖ Sep 21 '13 at 19:11
  • @g-makulik Unfortunately your link is down. But I *think* I understand what you're saying, I'm gonna try a few things and I'll report if successful. – syam Sep 21 '13 at 19:19
  • Sorry I just copied it from one of my header references, yes it's no longer available obviously. I found a working (at least for me) link for the document here: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.38.6457&rep=rep1&type=pdf – πάντα ῥεῖ Sep 21 '13 at 19:49
  • Tricky. If I could solve it for `static` methods of some type would it be enough? Because there we can make the method dependent then use the `template` keyword to force `template` parsing. – Yakk - Adam Nevraumont Sep 21 '13 at 20:05
  • @g-makulik Thanks for the link, I'll read it and try to see what I can make of it. – syam Sep 21 '13 at 20:07
  • What @Yakk refers to would be solvable with the concept check proposal I referred to. – πάντα ῥεῖ Sep 21 '13 at 20:14
  • @Yakk Unfortunately I have no control over the namespace `foo`, so I really have to detect free functions (indeed, `template` was one of the things I tried out of despair but of course it doesn't work here). To be frank, this whole thing is to seamlessly handle different versions of a third-party library that has no usable versioning information. Currently I use compile-time macro definitions to handle the different cases but this puts the burden on the users of *my* code, hence my trying to automate the detection. – syam Sep 21 '13 at 20:14
  • a serious hack approach would be to craft a statement that is parsed one way if you have a `template` and another if you do not. Not sure how. So the `<` would be less in one parse and the start of a `template` argument list in the other. The close waka would then be greater than. But what do you put next? And what do you put inside? Anyhow, what you want may be somewhat famous as an example of a parse that would need the `template` keyword in a dependent context to know what kind of expression it is. Look for those maybe. – Yakk - Adam Nevraumont Sep 21 '13 at 20:23
  • I'm afraid the main difference is the class and namespace scopes. I think the concept check examples I referred to, are mainly CRTP based, which is a concept that can't be applied to namespaces AFAIK. – πάντα ῥεῖ Sep 21 '13 at 20:37
  • We really need a kind of `compiles { some_code; }` keyword that returns a `bool` at compile-time instead of all those SFINAE workarounds. @Yakk your hackish approach is interesting but I doubt I can pull this up. As to the second part, not sure I understand what you mean. – syam Sep 21 '13 at 20:37
  • 1
    @g-makulik I think you're right WRT the paper you linked to, this probably doesn't apply in the case I describe here. I'm still trying to make full sense of it though, but since it touches another topic of interest to me (concepts) it makes it quite hard to correctly separate the issues at hand. In short: I'm really confused by all this, that's too much information at once for my poor brain on a Saturday night. :( – syam Sep 21 '13 at 20:59
  • @syam _'that's too much information at once for my poor brain on a Saturday night. :('_ Yes! – πάντα ῥεῖ Sep 21 '13 at 21:07
  • @g-makulik Especially since it's way past pub'o'clock, which doesn't help at all. ;) – syam Sep 21 '13 at 21:13
  • @syam Completely OT: I always like watching the all over world "pub'o'clock' time 'discontinuations', I'm just in the shadow zone now ;o) ... – πάντα ῥεῖ Sep 21 '13 at 21:20
  • @syam the `template` disambiguation keyword use is nre. Does it just make things easier to parse, or is it essential? If essential, there is an expression that is legal as both a `template` expression and as a literal expression. Look for that, and maybe some creative `using` could solve your problem. – Yakk - Adam Nevraumont Sep 21 '13 at 23:44

1 Answers1

2

Following may help you:

// Helper macro to create traits to check if function exist.
// Note: template funcName should exist, see below for a work around.
#define HAS_TEMPLATED_FUNC(traitsName, funcName, Prototype)                          \
    template<typename U>                                                             \
    class traitsName                                                                 \
    {                                                                                \
        typedef std::uint8_t yes;                                                    \
        typedef std::uint16_t no;                                                    \
        template <typename T, T> struct type_check;                                  \
        template <typename T = U> static yes &chk(type_check<Prototype, &funcName>*); \
        template <typename > static no &chk(...);                                    \
    public:                                                                          \
        static bool const value = sizeof(chk<U>(0)) == sizeof(yes);                  \
    }

So with provided namespace with bar and without bar2

// namespace to test
namespace foo {
    template<typename T, typename U> T bar(U);
    // bar2 not present
}

Code which check the presence of bar<int, int> and bar2<int, int>.

// dummy class which should be never used
namespace detail {
    struct dummy;
}

// Trick, so the names exist.
// we use a specialization which should never happen
namespace foo {
    template <typename T, typename U>
    std::enable_if<std::is_same<detail::dummy, T>::value, T> bar(U);

    template <typename T, typename U>
    std::enable_if<std::is_same<detail::dummy, T>::value, T> bar2(U);
}

#define COMMA_ , // trick to be able to use ',' in macro

// Create the traits
HAS_TEMPLATED_FUNC(has_foo_bar, foo::bar<T COMMA_ int>, int(*)(int));
HAS_TEMPLATED_FUNC(has_foo_bar2, foo::bar2<T COMMA_ int>, int(*)(int));

// test them
static_assert(has_foo_bar<int>::value, "something went wrong");
static_assert(!has_foo_bar2<int>::value, "something went wrong");
Jarod42
  • 203,559
  • 14
  • 181
  • 302