tags:

views:

340

answers:

6

I am in the process of designing a C++ static library. I want to make the classes generic/configuarable so that they can support a number of data types(and I don't want to write any data type specific code in my library). So I have templatized the classes.

But since the C++ "export" template feature is not supported by the compiler I am currently using, I am forced to provide the implementation of the classes in the header file. I dont want to expose the implementation details of my Classes to the client code which is going to use my library.

Can you please provide me with some design alternatives to the above problem??

A: 

You can try to obfuscate your code - but you have little choice in C++03 asides from including template code in header files.

Vandevoorde does describe another technique in his book: Explicit instantiation - but that entails having to explicitly instantiate all possible useful combinations.

But for the most comprehensive review of this topic read chapter 6 from C++ Templates: The Complete Guide.

Edit (in response to your comment): You have two options for writing generic code without using templates:
1) Preprocessor - still requires header files
2) using void* - yuk - incredibly unsafe

So no, i do not recommend not using templates for solving problems that templates were specifically designed (albeit somewhat flawed) for.

Faisal Vali
can you suggest me something , so that i can avoid templates??
sourabh jaiswal
and thus by avoiding templates ...bypass this problem...
sourabh jaiswal
@sourabh: you can overload the same function with all the different types you want to use (polymorphism) but it would probably go against many good-practice 'rules' at least one of them being DRY.
Daniel
I cannot use explicit instantiation that would be using the possible/would be data types in the library code....
sourabh jaiswal
@daniel : Dude i cannot use the possible data types in the library code...
sourabh jaiswal
A: 

Hello Sourabh,

I'm not so familiar with C++ but here is some useful links:

Hope i'm helping you!

Nathan Campos
Why the down vote???????
Nathan Campos
@Nathan - probably because you answered a C++ question despite having no idea what the answer is, and saying as much in your answer.
Daniel Earwicker
+3  A: 

Prior to templates, type-agnostic C++ code had to be written using runtime polymorphism. But with templates as well, you can combine the two techniques.

For example, suppose you wanted to store values of any type, for later retrieval. Without templates, you'd have to do this:

struct PrintableThing
{
    // declare abstract operations needed on the type
    virtual void print(std::ostream &os) = 0;

    // polymorphic base class needs virtual destructor
    virtual ~PrintableThing() {}
};

class PrintableContainer
{
    PrintableThing *printableThing;

public:
    // various other secret stuff

    void store(PrintableThing *p);
};

The user of this library would have to write their own derived version of PrintableThing by hand to wrap around their own data and implement the print function on it.

But you can wrap a template-based layer around such a system:

template <T>
struct PrintableType : PrintableThing
{
    T instance;

    virtual void print(std::ostream &os)
        { os << instance; }

    PrintableType(const T &i)
        : instance(i) {}
};

And also add a method in the header of the library, in the declaration of the PrintableContainer class:

template <class T>
void store(const T &p)
{
    store(new PrintableType(p));
}

This acts as the bridge between templates and runtime polymorphism, compile-time binding to the << operator to implement print, and to the copy-constructor also (and of course also forwarding to the nested instance's destructor).

In this way, you can write a library entirely based on runtime polymorphism, with the implementation capable of being hidden away in the source of the library, but with a little bit of template "sugar" added to make it convenient to use.

Whether this is worth the trouble will depend on your needs. It has a purely technical benefit in that runtime polymorphism is sometimes exactly what you need, in itself. On the downside, you will undoubtedly reduce the compiler's ability to inline effectively. On the upside, your compile times and binary code bloat may go down.

Examples are std::tr1::function and boost::any, which have a very clean, modern C++ template-based front end but work behind the scenes as runtime polymorphic containers.

Daniel Earwicker
A: 

One problem with templates is that they require compiled code. You never know how the end-user will specialize/instantiate your templates, so your dll-file would have to contain all possible template specializations in compiled form. This means that to export pair<X,Y> template you would have to force the compilication of pair<int,float>, pair<int,string>, pair<string,HWND> and so on... to infinity..

I guess more practical solution for you would be to un-template private/hidden code. You can create special internal functions that would only be compiled for single template specialization. In the following example internal_foo-function is never called from MyClass where A is not int.

template<class A>
class MyClass
{
  int a;
  float b;
  A c;

  int foo(string param1);
  {
     ((MyClass<int>*)this)->internal_foo(param1);
  }
  int internal_foo(string param1);  // only called on MyClass<int> instances
};

template<>
__declspec(dllexport) int MyClass<int>::internal_foo(string param1)
{
  ... secret code ...
}

This of course is a hack. When using it you should be extra careful not to use member variable "c", because it's not always integer (even though internal_foo thinks that it is). And you can't even guard yourself with assertions. C++ allows you to shoot yourself in the foot, and gives you no indications about it until it's too late.

PS. I haven't tested this code so it might require some fine tuning. Not sure for example if __declspec(dllimport) is needed in order for compiler to find internal_foo function from dll-file...

AareP
+1  A: 

I've got some news for you, buddy. Even with export, you'd still have to release all of your template code -- export just makes it that you don't have to put the definitions in a header file. You're totally stuck. The only technique you can use is split off some functions that are non-templates and put them into a different class. But that's ugly, and usually involves void* and placement new and delete. That's just the nature of the beast.

rlbond
"usually involves void* and placement new and delete" - not if it's done right. No need at all to do that.
Daniel Earwicker
A: 

With templates you cannot avoid shipping the code (unless your code only works with a fixed set of types, in which case you can explicitly instantiate). Where I work we have a library that must work on POD types (CORBA/DDS/HLA data definitions), so at the end we ship templates.

The templates delegate most of the code to non-templated code that is shipped in binary form. In some cases, work must be done directly in the types that were passed to the template, and cannot thus be delegated to non-templated code, so it is not a perfect solution, but it hides enough part of the code to make our CEO happy (the people in charge of the project would gladly provide all the code in templates).

As Neil points in a comment to the question, in the vast majority of cases there is nothing magical in the code that could not be rewritten by others.

David Rodríguez - dribeas