2

I have a utils.h and a utils.cpp that contain utility functions, all surrounded by a common namespace. When I directly include the utils.h into a project, I can access all these functions.

utils.h:

namespace Utils
{
  template <class T>
  T interpolationLinear ( array<T,1>^ i_aData,
                          double      i_dRatio)
  {
    array<T,1>^ aPart = gcnew array<T,1>(2);
    aPart[0] = i_aData[0] * (1 - i_dRatio);
    aPart[1] = i_aData[1] *     (i_dRatio);
    return aPart[0] + aPart[1];
  }
  ...
  double parseToDouble (System::String ^sValue);  // defined in cpp
}

Now I am creating several managed DLLs and the utility functions should become one of them. The problem: I can not access the functions in the DLL/assembly any more. Seems like only classes can build the DLL interface.
I have then changed the namespace to a public ref class and made the functions static, so I got the access to some of the functions back:

utils.h:

public ref class Utils
{
  template <class T>
  static T interpolationLinear (array<T,1>^ i_aData,
                                double      i_dRatio)  // NO ACCESS
  // like above
  ...
  static double parseToDouble (System::String ^sValue);  // CAN BE ACCESSED NOW
};

But why can't I call the template function? What is going wrong here?

nvoigt
  • 75,013
  • 26
  • 93
  • 142
Tobias Knauss
  • 3,361
  • 1
  • 21
  • 45
  • Is it because there is no call to the templated functions and therefore they are not instantiated, so there are no functions at all to build the interface? If so, how could I solve that? Instantiate them manually? – Tobias Knauss Nov 18 '13 at 13:51

2 Answers2

2

You are running into a well-known limitation of C++ templates, they don't have external linkage. You solve that in C++ by putting the template definition into a .h file and #include that file in other C++ projects. That of course cannot work here, a managed compiler doesn't know beans about a .h file, only a C++ compiler can ever parse them.

This would have been a major limitation in the .NET Framework, it supports many different languages and the type erasure implementation of templates as used in C++ and Java would not work well in practice. In other words, a language like VB.NET could not use the templates written in C#, etcetera.

So they took an entirely different approach, called reified generics. Big difference with type erasure is that the generic method is created at runtime instead of compile time. Or in other words, the managed compiler creates a "cookie-cutter" model of the method, it is made concrete at runtime when it is just-in-time compiled. With the cookie-cutter code identical to any language, expressed in MSIL so it is callable by any language that supports generics.

In C++/CLI you use this kind of generics simply by substituting template with generic. Usually some additional syntax is required to set the type constraints with the where keyword, but just the substitution is enough in your case.

This doesn't actually work, you'll now discover a limitation that's specific to reified generics. The compiler is going to complain about the * operator. There's is no generic version of this operator. The underlying problem is that are just a few actual types that have this operator. In .NET only int, long, float and double have it. Or to put it another way, there are very few actual T's you could use when you call the method.

Notable is that you can actually use that operator on more types in code. It for example works fine to multiply byte or decimal. But that requires non-trivial conversions, a byte has to uplifted to int first. For decimal, it needs to discover that the type has a dedicated operator*() overload. These kind of non-trivial conversions are easy to do by a compiler that knows the type, but they cannot be expressed in the cookie-cutter code that the compiler generates for a generic method. So it simply isn't supported.

Long story short, you cannot make this method generic. Not much of a real problem, there truly are very few T's you'd ever use it with and the method is small. So simply provide overloads of the method, problem solved.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • Unfortunately I've just figured that out, too: C2676, binary operator '*': 'T' does not define this operator... And unfortunately I am mainly using this function with a custom type. Seems like I have to either overload or return to templates. Is there any (easy) way at all to do arithmetic operations with generic types? I guess no... – Tobias Knauss Nov 18 '13 at 14:36
  • Unfortunatly there is no interface or constraint you can set on generic parameters that support operators, so indeed your method body will not compile. This is a .NET problem with generics, it would not compile in C# either. – nvoigt Nov 18 '13 at 14:39
  • OK, at least it seems to be possible for primitive types: http://stackoverflow.com/questions/147646/solution-for-overloaded-operator-constraint-in-net-generics. But as I need it for a complex (custom) type, I will revert to templates. – Tobias Knauss Nov 18 '13 at 14:53
  • Sounds like you are still missing the fundamental limitation with templates. They are not usable to any language other than native C++. Fine if you use them in your internal implementation, do not expect them to work across assembly boundaries. Which was your original question. – Hans Passant Nov 18 '13 at 15:00
1

Templates in C++ and Generics in C# are fundamentally different concepts although they offer a solution to the same problem. C++ templates are compile time templates. That means you cannot export them the way you can export generics in C#. On the other hand, C++ templates can do a lot of wierd stuff that C# generics cannot do, because generics need to make sure they would compile for any type of T.

You created a template. That is a C++ compile time thing and once compiled will not change. You did not compile for a special T, so nothing is exported.

What you probably wanted to do is a generic like in C#. For generics, use the keyword generic instead of template.

generic <class T> static T interpolationLinear (array<T,1>^ i_aData, double i_dRatio)
nvoigt
  • 75,013
  • 26
  • 93
  • 142
  • Thats absolutely what I'm looking for! Thanks a lot! Didn't know yet that C++/Cli supports that. – Tobias Knauss Nov 18 '13 at 14:01
  • You edited the question to hide the problem with your solution. That wasn't very helpful, the OP can't actually make this work. – Hans Passant Nov 18 '13 at 14:31
  • @HansPassant I edited the questions title and removed the tag because it already IS a tag. My edit did not change the question in any way. – nvoigt Nov 18 '13 at 14:35
  • I see it in the edit history, sorry about that. Truly bizarre, I definitely saw the post collapsed to just the ref class. Must be my old eyes playing tricks on me. – Hans Passant Nov 18 '13 at 14:49