lSoleyl
lSoleyl

Reputation: 329

How to solve circular dependency of structures to types of other structures, defined in a header only library (C++)?

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

Upvotes: 2

Views: 125

Answers (2)

Chris Beck
Chris Beck

Reputation: 16204

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.)

Upvotes: 4

Barry
Barry

Reputation: 303527

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{});

Upvotes: 4

Related Questions