1

This problem is complex to describe. I have a cross-platform application. Currently I am using Visual Studio 2012, GCC 4.8.x, and Clang 3.5. The application consists of many dynamic libraries so pieces of the problem are spread through various assemblies.

I have looked at these stack overflow questions, but they do not seem to address this issue:

  1. Clang can't handle a template specialization using referenced template template, but GCC can
  2. template specialization, different behavior on windows vs gcc?
  3. Template specialization and instantiation
  4. Template specialization and enable_if problems
  5. Explicit template specialization in g++ causing troubles

I have a small army of templated functions each with a unique specialization (using type traits, mostly). These templated functions take types and convert them to special strings (std::string ToString(T x)) and take such strings and convert them back to their types (T StringTo<T>(std::string x)). I have other templated classes that use these special conversions for their types.

Some dynamic libraries (plugins) may want to add new types and conversions for them. Great. To help with this, I wrote an evil macro (though the problem exists without the macro at all, just full disclosure here.)

The Problem

When compiling on Visual Studio 2012, everything works great. Switch over to GCC/Clang, and things start to go bad. If I keep the implementation identical, I get a problem where the compiler cannot find a matching overload for the new type. I have tried to #ifdef WIN32 my way out of this, but no luck. I cannot find a path that gets me the new templated overloads.

I suspect this might be because GCC/Clang does not deal with template specializations being spread among libraries well (and this seems like a reasonably difficult problem for compiler writers)...but I do not want to accept defeat. I even tried setting compiler options on Clang (-fms-compatibility and -fdelayed-template-parsing) with no success. (http://clang.llvm.org/docs/MSVCCompatibility.html)

Compiler Output

Here is a sample of the compiler output, though names were changed to protect the innocent and relate to the pseudo-code below. There are many many variations of this message (basically one per specialization):

/path/utilities/ToString.h:1014:15: note:   template argument deduction/substitution failed:
/path/utilities/ToString.h: In substitution of ‘template<class T> std::string ToString(const T&, typename std::enable_if<((std::is_same<T, char>::value || std::is_same<T, signed char>::value) || std::is_same<T, unsigned char>::value), T>::type*) [with T = Bar]’:
/path/core/Foo.h:156:49:   required from ‘std::string Foo<T>::getValue() const [with T = Bar; std::string = std::basic_string<char>]’
/path/plugin/Bar.cpp:201:1:   required from here
/path/utilities/ToString.h:1014:15: error: no type named ‘type’ in ‘struct std::enable_if<false, Bar>’
/path/core/Foo.h: In instantiation of ‘std::string Foo<T>::getValue() const [with T = Bar; std::string = std::basic_string<char>]’:
/path/plugin/Bar.cpp:201:1:   required from here
/path/utilities/ToString.h:1029:15: note: template<class T> std::string ToString(const T&, typename std::enable_if<(((std::is_convertible<T*, std::basic_string<char> >::value && (! std::is_same<T, char>::value)) && (! std::is_same<T, signed char>::value)) && (! std::is_same<T, unsigned char>::value)), T>::type*)
   std::string ToString(const T& x, typename std::enable_if<std::is_convertible<T*, std::string>::value && !std::is_same<T, char>::value && !std::is_same<T, signed char>::value && !std::is_same<T, unsigned char>::value, T>::type*)
               ^

Pseudo Code

I have gone through many gyrations trying to move definitions around, separate template definitions from declarations, compiler flags...it's all a horrible blur.

Here is some code to demonstrate what I am doing:

Utilities.dll, ToString.h

// Prototype some templates (Makes GCC/Clang Happy.  Visual Studio did not require.)
template<typename T> typename std::enable_if<std::is_constructible<T, std::string>::value, T>::type StringTo(std::string x);
template<typename T> typename std::enable_if<std::is_same<T, float>::value, T>::type StringTo(std::string x);
template<typename T> typename std::enable_if<std::is_same<T, double>::value, T>::type StringTo(std::string x);

// ...and many more.

// Implement the templates
template<typename T> 
typename std::enable_if<std::is_constructible<T, std::string>::value, T>::type StringTo(std::string x)
{
  return T(x);
}

// ...and many more.

// Finally, define a macro to build more conversions from types later.
#define BUILD_MORE(V) \
  template<typename T>\
  typename std::enable_if<std::is_same<T, V>::value, T>::type StringTo(std::string x)\
  {\
    return specialConversionCodeHere;\
  }

Core.dll (dependent on Utilities.dll), Foo.h

  template<typename T>
  class Foo
  {
    T getValue()
    {
      // Use our specialized template.
      return StringTo<T>(this->value);
    }

    std::string value;
  }

Plugin.dll (dependent on Core.dll), Bar.cpp

  // Define a new class.
  class Bar...

  // Now use the fancy macro to build a StringTo conversion for it.
  BUILD_MORE(Bar)

  // Now use said conversion
  void foobar()
  {
    // Create the templated class (Foo) which uses a 
    // specialized template for class Bar conversion from string.
    // Prepare for horrible build errors outside of Visual Studio
    auto fb = new Foo<Bar>();
    auto x = fb->getValue(); 
  }

I added a github project that demonstrates the problem: https://github.com/DigitalInBlue/TemplateTest

Community
  • 1
  • 1
DiB
  • 554
  • 5
  • 19
  • See [this](http://stackoverflow.com/a/21112309/4074081) answer. You should declare specialization before method that uses it. – dewaffled Dec 18 '15 at 13:59
  • In this example, the specialization is declared in the `BUILD_MORE` macro, and is then used by the new class `Foo`. – DiB Dec 18 '15 at 14:14
  • it is used in `getValue` method and declared after it. [Here](http://en.cppreference.com/w/cpp/language/template_specialization) is better example - see snippet with `ERROR: explicit specialization of sort(Array)` comment – dewaffled Dec 18 '15 at 14:17
  • To me, it looks like `Foo` should cause the instantiation of the `Foo` template, which occurs after the new specialization for `StringTo` is defined by the `BUILD_MORE` macro. What am I missing here? The order looks "correct" to me. – DiB Dec 18 '15 at 14:26
  • Maybe you should try to make minimal example reproducing the problem. I just noticed that even output you provided complains about `ToString` function and your macro defines `StringTo`. – dewaffled Dec 18 '15 at 15:20
  • Also check if _declared before the first use ... in every translation unit in which such a use occurs_ piece applicable to your scenario – dewaffled Dec 18 '15 at 15:34
  • Here is a project that fully illustrates my problem. It follows basically the text in this post, works in vs2012, and does not work with gcc 4.8. https://github.com/DigitalInBlue/TemplateTest – DiB Dec 18 '15 at 19:07
  • I see now, your overloaded function is in a separate namespace. [Here](http://stackoverflow.com/q/18598862/4074081) is minimized version of your problem and explanation. – dewaffled Dec 18 '15 at 21:13

1 Answers1

1

The issue here is that MSVC and GCC/Clang do resolve the templates differently. Look here at the post marked "HUGE EDIT": Overload Resolution in a Namespace

I posted an example project on GitHub that illustrates the problem. By changing the order that template overloads are defined in the final executable vs. included, the error is resolved on all the compilers I tested on. Essentially:

// First
#include <original set of templates and specializations>

// Next
// Define new specialization.

// Then
#include <class that uses the specialization, possibly from another assembly>

// Finally
// Code that uses the #include'ed class that uses the specialization.

I will update the github project to show the "before" and "after"

Community
  • 1
  • 1
DiB
  • 554
  • 5
  • 19