3

I am currently playing on a project using Boost.ProgramOptions and I had to create the following structure to add some constraints on an option:

template <const char *str1, const char*... str2> 
struct restrictedValues
{
...
};

In order to validate that new option you must overload the boost::program_options::validate function:

template<class T, class charT>                                                                            
void validate(boost::any& v, const std::vector< std::basic_string<charT> >& xs, T*, long);

The call for this validate function is the following:

validate(value_store, new_tokens, (T*)0, 0);

As precised by boost: "The target type is specified via a parameter which has the type of pointer to the desired type. This is workaround for compilers without partial template ordering, just like the last 'long/int' parameter."

I, hence, wrote my validate version in the following way:

template<class charT, const char *... str>
void validate(boost::any &v,
        const std::vector<std::basic_string<charT> >& values,
        restrictedValues<str...>* /*target_type*/,
        int /*unused*/) { ... }

It looks like my version of clang (Ubuntu clang version 3.5.0-4ubuntu2~trusty2 (tags/RELEASE_350/final) (based on LLVM 3.5.0)) simply skip my version and gracefully fail in the default version. Whilst my gcc ((Ubuntu 4.8.2-19ubuntu1) 4.8.2) happily compile.

EDIT See a live example that shows the different behaviour, credits @dyp:

Live On Coliru

#include <boost/any.hpp>
#include <vector>
#include <string>
#include <iostream>

template <const char *str1, const char*... str2> struct restrictedValues                                                                                
{
/*...*/
};

template<class T, class charT>                                                                            
void validate(boost::any&, const std::vector< std::basic_string<charT> >&, T*, long)
{
    std::cout << "default version\n";
}

extern char const client[] = "hello";
extern char const server[] = "world";

template<class charT, const char *... str>
void validate(boost::any &,
        const std::vector<std::basic_string<charT> >&,
        restrictedValues<str...>* /*target_type*/,
        int /*unused*/) {
    std::cout << "custom version\n";
}

int main()
{
    boost::any a;
    std::vector<std::string> xs;
    restrictedValues<client, server>* p = 0;
    validate(a, xs, p, 0);
}

Moreover the same process using non-variadic templates (a fixed number of const char*) for the structure/function does work like a charm.

I am not quite sure which lookup process leads to such a ambiguous error. If my function didn't use template, it would be selected according to the overloading rules, but that isn't the case. By reading the partial ordering rules for template functions, both functions have the same specialization for the template parameters, but my expectation would be that the int/long trick should work. Any idea on how to solve this template mystery?

user703016
  • 37,307
  • 8
  • 87
  • 112
Jiwan
  • 731
  • 4
  • 11
  • My bad wrong copy paste. Corrected it. The question is still the same. – Jiwan Apr 11 '15 at 15:05
  • I've seen this before. And similarly with `operator<<`s or Boost Serialization's `serialize/load/save` customization points. To be honest, I think this is a very interesting question, but it won't get the attention it deserves unless there's a SSCCE included. I think it could be in <20 lines.Can you add one? – sehe Apr 11 '15 at 16:31
  • @sehe [Like that](http://coliru.stacked-crooked.com/a/18dcea85cb925bae)? (I was curious myself :) – dyp Apr 11 '15 at 16:36
  • @dyp Exactly. Added. – sehe Apr 11 '15 at 16:39
  • 1
    Clang 3.6.0 chooses the custom version, not only for the exact code in the question, but also if you change `long` to `int` in the signature of the default version, to force the use of the partial ordering rules. – bogdan Apr 11 '15 at 18:09
  • @bogdan Interesting! Is it an extension in clang++3.6, a bug fix or an implementation of a proposed resolution of an issue in the Standard? [This MCVE](http://coliru.stacked-crooked.com/a/9b5ca216fd5f7d11) fails to compile on clang++3.5 – dyp Apr 11 '15 at 18:55
  • 2
    @dyp I compiled it with `-Wall -Wextra -std=c++14 -pedantic`, so I don't think it's an extension. GCC 4.9.2, MSVC12 and 14 all compile the code with no warnings (this [super-simplified version](http://coliru.stacked-crooked.com/a/5c7adc0eea00195d) derived from yours). I'd say the arguments for that parameter pack should be deduced, and then substitution obviously should work. I vote for a bug fix. – bogdan Apr 11 '15 at 21:03

1 Answers1

0

The usual approach here is to make ADL work using strong typedefs.

I've documented this in an older answer¹:


¹ the first comments there are obsolete and refer to the old answer I had before.

Community
  • 1
  • 1
sehe
  • 374,641
  • 47
  • 450
  • 633
  • I am not sure if that helps the OP. OP is trying to deduce a pack of strings, which clang fails to do. – dyp Apr 11 '15 at 16:39
  • @dyp I wouldn't be surprised overload to be disregarded because std::vector and std::basic_string associate the `std` namespace. I'm just putting this out here. Later I might have time to actually give it a test – sehe Apr 11 '15 at 16:41
  • I think the reason it fails is because the class template has a non-pack template parameter plus a pack, and OP is deducing a pack only. [works with matching parameters in deduction](http://coliru.stacked-crooked.com/a/6120d23b148930b0); [fails without additional template param in clang](http://coliru.stacked-crooked.com/a/9b5ca216fd5f7d11) – dyp Apr 11 '15 at 16:43
  • Adding a non-pack parameter do solves my problem. But it remains unclear as to why the a full variadic template isn't deduced. – Jiwan Apr 12 '15 at 15:09