0

Hi I'm having trouble getting to grips with how to organize my template classes in files. I've looked at C++ templates, undefined reference and am trying to use the tip given in http://www.parashift.com/c++-faq-lite/templates.html#faq-35.12 to separate my header and implementation files.

    #include "Variables.h"

    int main() {
Pressure<float,double> first(7.9,"atm");
return 0;
    }

Variable is my top level class, I have removed some functions for brevity.

 /*
 * Variable_Parent.h
 *
 * Here an abstract base class from which variables can be derived is defined.
 *
 */

#ifndef PARENT_VARIABLE_H_
#define PARENT_VARIABLE_H_

#include <string>
#include <map>

template <typename V,typename D> // To let different variables use different S.F.
struct Variable{
    /*
     * This structure is a abstract base class from which all dimensional variables
     * are derived. Derived variables may be operated with and units will be largely
     * automatically handled.
     */

protected:
    //Functions
    Variable(V v, std::string u, D conversions [], std::map<std::string,short> units);
    ~Variable();

    //Variables
    V value;
    std::string unit;
    std::map<std::string, unsigned short> * valid_units; //contains all units for which conversions have been defined.
    D * conversion; //An n by n array for calculating conversions

public:
    V get_value() const;
    std::string get_unit() const;
    void change_unit(std::string new_unit);


        #endif /* PARENT_VARIABLE_H_ */

My implementation:

    /*
     * Header files
     */
    #include "Variables_Parent.h"
    #include <string>
    #include <map>
    #include <typeinfo>

    template<typename V,typename D>
    Variable<V,D>::Variable(V v, std::string u, D conversions [], std::map<std::string,short> units){
        value = v;
        unit = u;
        conversion = conversions;
        valid_units = &units;
    }

    template <typename V,typename D>
    V Variable<V,D>::get_value() const{
            return this.value;
        }

    template <typename V,typename D>
    std::string Variable<V,D>::get_unit() const{
        return unit;
    }

template <typename V,typename D>
void Variable<V,D>::change_unit(std::string new_unit){
    if (valid_units->find( new_unit ) == valid_units->end()){//Check the unit is defined
        std::string message("%s is not a valid unit. /n", new_unit);
        warning(message);
    }
    else{
        int target = (*valid_units)[new_unit];
        int original = (*valid_units)[unit];
        int width = valid_units->size();
        value*=conversion[ (original*width) + target];
        unit=new_unit;
    }
}

Now I want to be able to create specialized instances of this class which differ in where valid_units and conversion point to, I'm aware I could give this as an option in the constructor but as many of these may be created I wanted a simple constructor. So:

/*
 * Variables.h
 *
 * Here all the variable classes are defined.
 *
 *  Created on: Nov 16, 2011
 *      Author: 
 */

#ifndef VARIABLES_H_
#define VARIABLES_H_

#include "Variables_Parent.h"
#include <string>


///////////////////////////////////////////////////////////////////////////////////////////////////////
/*
 * Pressure
 */
template <typename V,typename D>
class Pressure : Variable<V,D> {
public:
    Pressure(V v,std::string u);
    ~Pressure();
};

In the implementation file at the bottom I've commented out two template class declarations I was trying to use as suggested by the parashift FAQ, these lines give me the the console error:

Invoking: GCC C++ Compiler
g++ -O0 -g3 -Wall -c -fmessage-length=0 -MMD -MP -MF"src/Variables.d" -MT"src/Variables.d" -o"src/Variables.o" "../src/Variables.cpp"
../src/Variables.cpp: In constructor ‘Pressure<V, D>::Pressure(V, std::string) [with V = float, D = double, std::string = std::basic_string<char>]’:
../src/Variables.cpp:27:16:   instantiated from here
../src/Variables.cpp:22:43: error: no matching function for call to ‘Variable<float, double>::Variable()’
../src/Variables_Parent.h:26:2: note: candidates are: Variable<V, D>::Variable(V, std::string, D*, std::map<std::basic_string<char>, short int>*) [with V = float, D = double, std::string = std::basic_string<char>]
../src/Variables_Parent.h:17:16: note:                 Variable<float, double>::Variable(const Variable<float, double>&)
../src/Variables.cpp:23:2: error: no matching function for call to ‘Variable<float, double>::Variable(float&, std::string&, double [3], const std::map<std::basic_string<char>, short unsigned int>&)’
../src/Variables_Parent.h:26:2: note: candidates are: Variable<V, D>::Variable(V, std::string, D*, std::map<std::basic_string<char>, short int>*) [with V = float, D = double, std::string = std::basic_string<char>]
../src/Variables_Parent.h:17:16: note:                 Variable<float, double>::Variable(const Variable<float, double>&)
make: *** [src/Variables.o] Error 1

\

/*
 * Variables.c++
 *
 *  Created on: Nov 18, 2011
 *      Author: 
 */

#include "Variables.h"
#include <string>
#include <boost/assign.hpp>

///////////////////////////////////////////////////////////////////////////////////////////////////////
/*
 * Pressure
 */
const static std::map<std::string, unsigned short> PRESSURE_UNITS =
    boost::assign::map_list_of("kPa",0)("atm",1)("psi",2);// might want new

static double PRESSURE_CONVERSION[3][3]= {{1,0.009869232667,0.145037738},{101.325,1,14.6959488},{6.89475729,0.0680459639,1}};

template <typename V,typename D>
Pressure<V,D>::Pressure(V v, std::string u){
    Variable<V,D>(v,u,PRESSURE_CONVERSION[0],PRESSURE_UNITS);
}

/*
template class Pressure<float, double>;
template class Variable<float, double>;
/*

When I do not include the declarations in main i get an error on trying to create first. Multiple markers at this line - undefined reference to Pressure<float, double>::~Pressure()' - undefined reference toPressure::Pressure(float, std::basic_string, std::allocator

)'

Sorry about the length I wanted to make sure I included all relevant information given at this point I don't have a clue. Thanks in advance I hope I'm not going to have to move my implementation into my headers.

Community
  • 1
  • 1
wookie1
  • 511
  • 6
  • 18

2 Answers2

2

When using templates, implementation should be in the translation unit (most commonly the same header file) that defines the interface. There are workarounds but the fact is always there.

Michael Price
  • 8,088
  • 1
  • 17
  • 24
perreal
  • 94,503
  • 21
  • 155
  • 181
0

You might want to take a good look at this: http://www.amath.unc.edu/sysadmin/DOC4.0/c-plusplus/c%2B%2B_ug/Templates.new.doc.html From that article,

Because of the separation of header and template source files, you must be very careful in file construction, placement, and naming. You may also need to explicitly identify the location of the source file to the compiler.

Mike Warren
  • 3,796
  • 5
  • 47
  • 99