2

I want to write a conversion utility header, which can be used without much fuss, so it should be used as a header-only library (since it's so small it would be overkill to create a seperate .lib for it anyway).

Here is a sketch of the lib (Containing only UTF8 and UTF16 conversion)

namespace cp_utils {
  namespace cp { //codepages
   struct UTF8 {
     typedef std::string string;
     typedef const std::string& string_ref; //will be boost::string_ref later on 

     template<typename ToCP>
     static typename ToCP::string convertTo(string_ref str);

     template<>
     static UTF16::string convertTo<UTF16>(string_ref str) { //<- Problem here!
       //... convert UTF-8 string to UTF-16 wstring
       return L"UTF-16"; //Mock implementation
     }
   };

   struct UTF16 {
     typedef std::wstring string;
     typedef const std::wstring& string_ref;

     template<typename ToCP>
     static typename ToCP::string convertTo(string_ref str);

     template<>
     static UTF8::string convertTo<UTF8>(string_ref str) {
       //... convert UTF-16 wstring to UTF-8 string
       return "UTF-8"; //Mock implementation
     }
   };    

   ///... other codepages similar
  }

  //actual conversion function
  template<typename FromCP, typename ToCP>
  inline typename ToCP::string convert(typename FromCP::string_ref str) {
   return FromCP::convertTo<ToCP>(str);   
  }
}

//usage:
int main() {
  wstring utf16str = convert<cp::UTF8, cp::UTF16>("test");
}

The error message says C2065: "UTF16" undeclared identifier I tried to fix this by a forward declaration of the UTF16 struct with a simple struct UTF16;, but then the error states C2027: use of undefined type "UTF16". I somehow have to provide the full definition of UTF16 before defining UTF8 and vice-versa.

If I define the specialized conversion functions of UTF8 and UTF16 outside the structs, then it works fine as long as the library is used in only one .cpp file because of multiple definition, which makes this pretty impractial.

How can I solve this problem?

PS: I compiled the code with MSVS 2013

lSoleyl
  • 309
  • 1
  • 7

2 Answers2

4

The conversion functions don't have to be members. If you make them free functions, then you don't have to worry about forward declaring or circular references. Furthermore, you don't even need to have function templates. Just pass what you're converting to as an empty type argument:

struct UTF8 {
    using string = std::string;
};

struct UTF16 {
    using string = std::wstring;
};

// 8 --> 16
UTF16::string convertTo(UTF8::string const& from, UTF16 );

// 16 --> 8
UTF8::string convertTo(UTF16::string const& from, UTF8 );

Usage there being like:

UTF8::string some_string = ...;
auto as_utf_16 = convertTo(some_string, UTF16{});
Barry
  • 286,269
  • 29
  • 621
  • 977
  • 1
    You are right, they don't have to be members, but I wanted to organize the code somehow and keep the implementation close to the actual codepage. As to your suggestion: If I would implement conversion functions between let's say CP1252 and UTF-8 (both would use std::string as underlying type) wouldn't it lead to function ambiguity? – lSoleyl Aug 31 '15 at 18:14
  • 1
    @lSoleyl In that case, you could take a from type and a to type. Overloading is simpler than template specialization. – Barry Aug 31 '15 at 18:17
  • I see that overloading is simpler, but template specialization allows me to check whether the caller has passed two identical encodings and in that case return the value itself: `convert("test") -> "test"`. As far as I know this isn't possible for overloading without explicitly defining a conversion function for each codepage, to the same codepage. – lSoleyl Aug 31 '15 at 18:28
  • 1
    @lSoleyl Of course it's possible. `template Str convertTo(Str const& string, T /* from */, T /* to */) { return string; }` – Barry Aug 31 '15 at 18:30
  • Indeed, sometimes the solution is too simple to see... I will stick with Chris' solution though, since I have to seperate the conversion definition from the type definition for both suggested solutions and I personally prefer the use of templates. – lSoleyl Aug 31 '15 at 18:37
4

This is actually a duplicate of this question Circular Dependencies / Incomplete Types and the general "how do I resolve circular dependencies in C / C++" question, it's just a little harder to see because there are templates involved.

Basically you should move the implementations of both of these functions

template<>
 static UTF16::string convertTo<UTF16>(string_ref str) { //<- Problem here!
   //... convert UTF-8 string to UTF-16 wstring
   return L"UTF-16"; //Mock implementation
 }


 template<>
 static UTF8::string convertTo<UTF8>(string_ref str) {
   //... convert UTF-16 wstring to UTF-8 string
   return "UTF-8"; //Mock implementation
 }

out of the class bodies and leave only the declarations, and put those at the end of the file. (But still in the same namespace.)

Community
  • 1
  • 1
Chris Beck
  • 15,614
  • 4
  • 51
  • 87
  • I already tried that. This leads to the problem that I can't use the header file in more than one .cpp file, beacuse I get the multiple definitions error. – lSoleyl Aug 31 '15 at 18:18
  • 2
    I think you could solve this by marking the functions "inline", or by modifying the syntax to what Barry suggests. – Chris Beck Aug 31 '15 at 18:22
  • Marking them as inline solved it... Even though I'm not quite happy that I have to seperate the type definition from the definition of the conversion functions. – lSoleyl Aug 31 '15 at 18:31