views:

71

answers:

2

The goal of this project is to create a library for distribution. In the past, I used forward declares so I didn't have to distribute a bunch of header files along with the libraries. However, I'm now trying to eliminate code duplication by switching to templates and am running into some issues.

First, a simple example project showing what is currently working:

//LibraryDep1.h

class LibraryDep1
{
public:
    LibraryDep1(void) {};
    virtual ~LibraryDep1(void) {};

    template <typename T>
    int TestFunction(T value)
    {
        std::cout << value << std::endl;
        return 0;
    }
};


//LibraryInclude.h

class LibraryDep1; //forward declare

class LibraryInclude
{
private:
    LibraryDep1* mLibDep1;
public:
    LibraryInclude(void);
    virtual ~LibraryInclude(void);

    int TestFunction(int value);
    int TestFunction(std::string value);


};

//LibraryInclude.cpp

#include "LibraryInclude.h"
#include "LibraryDep1.h"


LibraryInclude::LibraryInclude(void)
{
    this->mLibDep1 = new LibraryDep1();
}


LibraryInclude::~LibraryInclude(void)
{
    delete this->mLibDep1;
}

int LibraryInclude::TestFunction(int value)
{
    return this->mLibDep1->TestFunction(value);
}

int LibraryInclude::TestFunction(std::string value)
{
    return this->mLibDep1->TestFunction(value);
}

//main.cpp
#include <tchar.h>
#include "LibraryInclude.h"

int _tmain(int argc, _TCHAR* argv[])
{
    LibraryInclude inclLibrary;
    inclLibrary.TestFunction(77);
    inclLibrary.TestFunction("test");
}

This gives the expected output of:

77
test

However, the overloads of LibraryInclude::TestFunction could be replaced with a template function to further reduce code duplication:

//LibraryInclude.h

class LibraryDep1; //forward declare

class LibraryInclude
{
private:
    LibraryDep1* mLibDep1;
public:
    LibraryInclude(void);
    virtual ~LibraryInclude(void);

    template <typename T>
    int TestFunction(T value) {
      return mLibDep1->TestFunction(value);
    }

};

The problem now is that I'm using mLibDep1 without including the full implementation giving me an undefined type compilation error. Meaning that I need to #include "LibraryDep1.h" in LibraryInclude.h, thus requiring me to distribute both LibraryInclude.h and LibraryDep1.h with my library. This is a simple example, the real project has many header files that would need to be distributed if I were to switch to using the templated version of LibraryInclude.

My question is, is there any way to avoid having to distribute a bunch of include files with my library and eliminate code duplication? Or, am I better off just overloading for all known types (drastically reducing library flexibility) in the distributed header file and keeping the templates in only the underlying classes?

+1  A: 

No. There is currently no way to do what you want. When compiler vendors start implementing the 'export' keyword you'll be in luck. Currently I only know of Comeau doing so. This keyword has been around for years so I wouldn't hold my breath until the rest implement it.

Noah Roberts
Very few vendors currently support the export feature (basically whomever uses EDG's frontent - i.e. Comeau) and the feature was voted to be removed from C++0x which will further shrink vendor support...
Eugen Constantin Dinca
I had read about the extern keyword, as well as its demise with C++0x. Seems like something similar would be useful for library writers, but people who know much more about these things than me made the decision to remove it, so I'm probably wrong.
bsruth
I believe it was removed because it was really hard to implement for some reason...along with the subsequent fact that nobody implemented it.
Noah Roberts
+1  A: 

A very limited and ugly solution would be:

//LibraryDep1.h

#pragma once
#include <iostream>

class LibraryDep1
{
public:
    LibraryDep1(void) {};
    virtual ~LibraryDep1(void) {};

    template <typename T>
    int TestFunction(T value)
    {
        std::cout << value << std::endl;
        return 0;
    }
};

//LibraryInclude.h

#pragma once

class LibraryDep1; //forward declare

class LibraryInclude
{
private:
   LibraryDep1* mLibDep1;

public:
    LibraryInclude(void);
    virtual ~LibraryInclude(void);

    template <typename T>
    int TestFunction(T value);
};

//LibraryInclude.cpp

#include "LibraryInclude.h"
#include "LibraryDep1.h"

#include <string>

LibraryInclude::LibraryInclude(void)
{
    mLibDep1 = new LibraryDep1();
}

LibraryInclude::~LibraryInclude(void)
{
}

// only to save some typing when only forwaring calls
#define LI_TESTFUNCTION( TYPE ) \
template<> \
int LibraryInclude::TestFunction<TYPE>( TYPE value ) {\
   return mLibDep1->TestFunction(value); \
}

// the allowed specializations, everything else causes link errors
LI_TESTFUNCTION( int );
LI_TESTFUNCTION( std::string );

Tested this with VC++ 2k8 & g++ 4.3.4 statically linking against LibraryInclude.o

Eugen Constantin Dinca
I looked into this method as well. But, as you said, it is an ugly solution.
bsruth
@bsruth: Without extern one needs to provide specializations for what's "allowed" so to make the linker happy...
Eugen Constantin Dinca