C++ - Defining class template (header/source file)

大城市里の小女人 提交于 2019-12-06 13:29:46

问题


I want to create a processor in voreen (like this one .cpp | .h) porting this OTB-Application:

http://hg.orfeo-toolbox.org/OTB/file/ca4366bb972e/Applications/Segmentation/otbSegmentation.cxx

I have coded almost all the parameters into properties, etc but..

If you look at like 376, you'll see a class template of FloatVectorImageType::SizeType, a typedef type.

Im not familiar with c++ templates so my first question was where should I put this template's implementation, in the .cpp or .h file of the processor? Taking a brief look at c++ tutorials and other processor examples like the one above, I figure out I have to declare the template in header and define it in the .cpp.

The problem is that the compiler doesnt allow me to define a template class of a typedef type inside the .cpp. The typedef isnt recognised..

So, can someone point me to the right direction here?

segmentationprocessor.h

#ifndef OTBSEGMENTATIONAPPLICATION_H
#define OTBSEGMENTATIONAPPLICATION_H

#include "otbVectorImage.h"
#include "modules/otb/ports/otbimageport.h"
#include "modules/otb/ports/otbvectorimageport.h"
#include "voreen/core/properties/boolproperty.h"

//..more includes here

namespace voreen {

class OTBSegmentationApplication : public OTBImageFilterProcessor
{
public:
    OTBSegmentationApplication();

    virtual ~OTBSegmentationApplication();

    virtual Processor* create() const;

    virtual std::string getCategory() const { return "Applications"; }
    virtual std::string getClassName() const { return "Segmentation Application"; }
    virtual CodeState getCodeState() const { return CODE_STATE_EXPERIMENTAL;}//STABLE, TESTING, EXPERIMENTAL

    virtual std::string getProcessorInfo() const;

    /** Images typedefs */
    typedef otb::VectorImage<double, 2> VectorImageType;
    typedef ImageType               LabelImageType;
    typedef ImageType               MaskImageType;

    typedef VectorImageType::SizeType size;

    // Segmentation filters typedefs
    // Edison mean-shift
    typedef otb::MeanShiftVectorImageFilter<VectorImageType,VectorImageType,LabelImageType> EdisonSegmentationFilterType;
    EdisonSegmentationFilterType::Pointer edisonFilter;

    // Home made mean-shift
    typedef otb::MeanShiftSegmentationFilter<VectorImageType, LabelImageType, VectorImageType> MeanShiftSegmentationFilterType;
    MeanShiftSegmentationFilterType::Pointer meanshiftFilter;

    // Simple connected components
    typedef otb::Functor::ConnectedComponentMuParserFunctor<VectorImageType::PixelType> FunctorType;

    typedef itk::ConnectedComponentFunctorImageFilter <VectorImageType, LabelImageType, FunctorType, MaskImageType> ConnectedComponentSegmentationFilterType;
    ConnectedComponentSegmentationFilterType::Pointer ccFilter;

    typedef itk::ScalarConnectedComponentImageFilter<LabelImageType, LabelImageType> LabeledConnectedComponentSegmentationFilterType;
    LabeledConnectedComponentSegmentationFilterType::Pointer labeledCCFilter;

    //..more typedefs here

protected:
    virtual void setDescriptions() {
        setDescription("Performs segmentation of an image, and output either a raster or a vector file. In vector mode, large input datasets are supported.");
    }
    void process();
    virtual void initialize() throw (tgt::Exception);
    virtual void deinitialize() throw (tgt::Exception);

    /** TEMPLATE DECLARATION (?) */

    template<class TInputImage, class TSegmentationFilter>
    VectorImageType::SizeType GenericApplySegmentation(otb::StreamingImageToOGRLayerSegmentationFilter
                                                       <TInputImage, TSegmentationFilter> * streamingVectorizedFilter, TInputImage * inputImage,
                                                       const otb::ogr::Layer& layer, const unsigned int outputNb);
    virtual void updateFilterSelection();
    virtual void updateModeSelection();

private:

    OTBVectorImagePort inPort_;
    StringOptionProperty filter_; ///< Select segmentation algorithm
    OTBVectorImagePort vectorOutPort_;
    OTBImagePort vectorMaskInPort_;
    OTBImagePort outPort_;

    //..more property definitions here

    static const std::string loggerCat_; ///< Category used in logging
};

} // namespace

#endif // OTBSEGMENTATIONAPPLICATION_H

segmentationprocessor.cpp

#include "segmentationprocessor.h"
#include "voreen/core/voreenapplication.h"

namespace voreen {

const std::string OTBSegmentationApplication::loggerCat_("voreen.OTBSegmentationApplication");

OTBSegmentationApplication::OTBSegmentationApplication()
    :OTBImageFilterProcessor(),
      inPort_(Port::INPORT, "IN Multiband Image", 0),
      vectorOutPort_(Port::OUTPORT, "OUT Multiband Image", 0),
      vectorMaskInPort_(Port::INPORT, "IN Mask Image", 0),
      outPort_(Port::OUTPORT, "OUT OTB Image", 0),

      filter_("selectFilter", "Segmentation algorithm"),

      //.. more properties code here

{
    addPort(inPort_);
    addPort(vectorOutPort_);
    addPort(vectorMaskInPort_);
    addPort(outPort_);

    addProperty(filter_);

    //.. adding the rest of properties here

    edisonFilter = EdisonSegmentationFilterType::New();
    meanshiftFilter = MeanShiftSegmentationFilterType::New();
    ccFilter = ConnectedComponentSegmentationFilterType::New();

    //..instantiating more filters needed in implementation here

}

Processor* OTBSegmentationApplication::create() const {
    return new OTBSegmentationApplication();
}

OTBSegmentationApplication::~OTBSegmentationApplication() {

}

void OTBSegmentationApplication::initialize() throw (tgt::Exception) {
    Processor::initialize();
}

void OTBSegmentationApplication::deinitialize() throw (tgt::Exception) {
    Processor::deinitialize();
}

std::string OTBSegmentationApplication::getProcessorInfo() const {
    return "Segmentation Application";
}

void OTBSegmentationApplication::updateFilterSelection() {
    //code for visual updates on properties here
}

void OTBSegmentationApplication::updateModeSelection() {
    //code for visual updates on properties here
}

    //TEMPLATE IMPLEMENTATION HERE (?)
template<class TInputImage, class TSegmentationFilter>
OTBSegmentationApplication::VectorImageType::SizeType OTBSegmentationApplication::GenericApplySegmentation(otb::StreamingImageToOGRLayerSegmentationFilter<TInputImage,
                             TSegmentationFilter> * streamingVectorizedFilter, TInputImage * inputImage, const otb::ogr::Layer& layer, const unsigned int outputNb)
    {
        typedef  TSegmentationFilter SegmentationFilterType;
        typedef  typename SegmentationFilterType::Pointer SegmentationFilterPointerType;
        typedef otb::StreamingImageToOGRLayerSegmentationFilter<TInputImage,SegmentationFilterType> StreamingVectorizedSegmentationOGRType;

    //..the rest of template code here
}


void OTBSegmentationApplication::process() {

    try
    {
        //PROCESSOR IMPLEMENTATION GOES HERE
        LINFO("Segmentation Application Connected");
    }
    catch (int e)
    {
        LERROR("Error in Segmentation Applicationn");
        return;
    }
}

}   // namespace

error: ‘VectorImageType’ does not name a type (fixed)


回答1:


I'm not familiar with c++ templates so my first question was where should I put this template's implementation, in the .cpp or .h file of the processor?

Put it in the header file. That's the simplest and most robust solution. Typically, you want to put definitions of functions (i.e. their function body) in source files (.cpp), as source files can be compiled independently. But this isn't possible for templates(*).

(*) slightly simplified.

Class templates are just blueprints for classes, function templates are blueprints for functions. That is, function templates are not functions, in other words "template function" is misleading, it's not a function but a template/blueprint.

The process of building a function from a function template (or class from a class template) is called instantiation. The result is an instantiated function, or, more generally, a function template specialization.

Template specializations are no templates. A function template specialization is just an ordinary function with a weird name; a class template specialization is just a class with a weird name.

A template will only be instantiated for some specific sets of template arguments:

  • Either if you explicitly ask it to be instantiated
  • or if you just use a specialization (-> implicit instantiation).

The second way is by far the more common. An example:

template<class T>
struct my_type
{
    T mem;
};

// using the specialization -> implicit instantiation
my_type<int> o;
my_type<double> p;

This will instantiate the class template my_type once for the template argument int, and once for the template argument double. This creates two independent and unrelated types with similar names: my_type<int> and my_type<double>

And similarly for functions; except that for functions, you typically do not explicitly provide the template arguments. Instead, you let the compiler deduce the template arguments from the types of the function arguments. Example:

template<class T>
void foo(T param)
{
    std::cout << param;
}

foo<int>(42);  // explicitly specifying the template arguments -- DON'T DO THAT
foo(21);       // template argument *deduction*

The second call will automatically deduce the template argument to be int. Again, we have created (implicitly instantiated) two functions with similar names: foo<int>(int) (the name is foo<int> and it has a single function parameter of type int) and foo<double>(double)


Why it's bad to put the definitions of templates in a source file: See Why can templates only be implemented in the header file?

Short version: As templates are blueprints, in order to use them, the compiler has to instantiate them. But it can only instantiate what it knows.

If you declare a function template foo in a header file templ.h, define it in templ.cpp and use it in main.cpp, then:

  • In main.cpp, the compiler doesn't know about the definition of the function template. It can only instantiate the declaration of foo, but not the definition.

  • In templ.cpp, the compiler does know about the definition, and can instantiate it. However, it doesn't know about the uses outside templ.cpp -- therefore it cannot instantiate it for all sets of arguments used outside.

In the example in the OP, that works, but it seems to be an oversight:

[templ.h]

template<class T>
void foo(T);

void ordinary_function();

[templ.cpp]

#include "templ.h"

template<class T>
void foo(T p)
{
    std::cout << p;
}

void ordinary_function()
{
    foo(42);     // implicit instantiation of foo<int>
    foo(2.5);    // implicit instantiation of foo<double>
}

[main.cpp]

#include "templ.h"
int main()
{
    foo(23);    // works fine, uses the foo<int> defined in templ.cpp
    foo('a');   // linker error: foo<char> not defined
    return 0;
}

Because the definition of foo<char> hasn't been instantiated in templ.cpp, and it cannot be instantiated in main.cpp, this produces a linker error.

That's why you should NOT rely on this behaviour. You can use explicit instantiation if you, for some reason, do not want to define the function template in the header file. At least, the explicit instantiation is explicit, and should be documented, so that no surprises happen.


The problem the compiler is actually complaining about has nothing to do with templates ;) It's just a name lookup issue. A simplified example:

#include <iostream>

struct Foo
{
    typedef int Bar;

    void do_something();
};

Bar Foo::do_something()
{
    std::cout << "something\n";
}

When the compiler sees the line Bar Foo::do_something() it sees Bar, and cannot find what that name refers to. Hence the error. On the other hand:

#include <iostream>

struct Foo
{
    typedef int Bar;

    void do_something();
};

Foo::Bar Foo::do_something()
{
    std::cout << "something\n";
}

Now you told the compiler where to look for the name Bar, namely, inside Foo.




回答2:


Here is a short example on templates which I wrote in order to help you:
in header:

typedef TemplatedClassName< ParameterValue0, ParameterValue1 > TemplateAlias;

in source file:
// explicit template instantiation

template class TemplatedClassName< ParameterValue0, ParameterValue1 >;


来源:https://stackoverflow.com/questions/19926280/c-defining-class-template-header-source-file

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!