Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

(Objective-)C++ Duplicate symbol on template specialization

Okay, I've been struggling with this weird error since yesterday, so I guess it's time to ask the community...

I'm currently working in Objective-C++, and I have a purely C++ class declaration in a header file like this:

#ifndef __MATRIX_H__
#define __MATRIX_H__

#define USE_NEON_UPSAMPLING2X true
#define USE_NEON_THRESHOLD true

typedef float OCRfloat;

template<class T = OCRfloat>
class Matrix {

public:
    ...

    Matrix threshold(T thresholdValue) const;

    ...

    Matrix upsample2x() const;

    ...
};

#ifdef TARGET_OS_IPHONE

#if USE_NEON_UPSAMPLING2X
template<> Matrix<float> Matrix<float>::upsample2x() const;
#endif

#if USE_NEON_THRESHOLD
template<> Matrix<float> Matrix<float>::threshold(float thresholdValue) const;
#endif

#endif

#include "Matrix.cpp"

#endif

It's template class, with basic matrix operations, however, I want to optimize some bottlenecks by template specialization on T=float. I have the included Matrix.cpp file as follows:

#include <iostream>
#include <cmath>

#if defined TARGET_OS_IPHONE
#include <Accelerate/Accelerate.h>
#endif

...

template<class T> Matrix<T> Matrix<T>::threshold(T thresholdValue) const {
   ... // general naive algorithm
}

template<class T> Matrix<T> Matrix<T>::upsample2x() const{
   ... // general naive algorithm
}

#ifdef TARGET_OS_IPHONE

#if USE_NEON_UPSAMPLING2X
template<> Matrix<float> Matrix<float>::upsample2x() const{
   ... // specialized for ARM NEON float32_t
}
#endif

#if USE_NEON_THRESHOLD
template<> Matrix<float> Matrix<float>::threshold(float thresholdValue)const{
   ... // specialized for ARM NEON float32_t
}
#endif

The problem is the following: If I define USE_NEON_UPSAMPLING2X=false and USE_NEON_THRESHOLD=true, everything works fine - the app is built, and works like charm. However, if I set USE_NEON_UPSAMPLING2X=true, the linker breaks with the following:

duplicate symbol __ZNK6MatrixIfE10upsample2xEv in:
    /.../OCRDemo-eggnlcolcwqycjagwvwddpfwgzlb/Build/Intermediates/OCRDemo.build/Release-iphoneos/OCRDemo.build/Objects-normal/armv7/A.o
    /.../OCRDemo-eggnlcolcwqycjagwvwddpfwgzlb/Build/Intermediates/OCRDemo.build/Release-iphoneos/OCRDemo.build/Objects-normal/armv7/B.o
duplicate symbol __ZNK6MatrixIfE10upsample2xEv in:
    /.../OCRDemo-eggnlcolcwqycjagwvwddpfwgzlb/Build/Intermediates/OCRDemo.build/Release-iphoneos/OCRDemo.build/Objects-normal/armv7/A.o
    /.../OCRDemo-eggnlcolcwqycjagwvwddpfwgzlb/Build/Intermediates/OCRDemo.build/Release-iphoneos/OCRDemo.build/Objects-normal/armv7/C.o
duplicate symbol __ZNK6MatrixIfE10upsample2xEv in:
    /.../OCRDemo-eggnlcolcwqycjagwvwddpfwgzlb/Build/Intermediates/OCRDemo.build/Release-iphoneos/OCRDemo.build/Objects-normal/armv7/A.o
    /.../OCRDemo-eggnlcolcwqycjagwvwddpfwgzlb/Build/Intermediates/OCRDemo.build/Release-iphoneos/OCRDemo.build/Objects-normal/armv7/D.o

The funny thing is that both methods are used in these files, however the linker only complains about upsample2x... The only difference between the two, from a syntactic point of view is the number of arguments: the threshold has an argument of T, while the upsample2x requires none - besides this, both are defined as const, both return matrices etc.

So my question is: what causes this cryptic error, and how can I fix it?

like image 667
Tamás Zahola Avatar asked Dec 06 '22 05:12

Tamás Zahola


2 Answers

Your error is twofold. First of all you write template code in a .cpp file. Then, to correct that error, you include the .cpp file in a header, another bad idea.

Why?

A class template is not really a class, yet, just a template for a group of classes. The classes that can be made from it are created as needed. A .cpp file, on the other hand, is only compiled once, which is not enough and not even meaningful, since you only have a template at this point.

On the other hand, to include a .cpp file is a bad thing in itself, since it typically results in recompilation of code that may not be recompiled, hence the link errors.

EDIT: Either you can define the specializations in a .cpp file (but no pure template code, and don't include it!), or you can inline them and keep them in the header:

#if USE_NEON_UPSAMPLING2X
template<> inline Matrix<float> Matrix<float>::upsample2x() const{
   ... // specialized for ARM NEON float32_t
}
#endif

#if USE_NEON_THRESHOLD
template<> inline Matrix<float> Matrix<float>::threshold(float thresholdValue)const{
   ... // specialized for ARM NEON float32_t
}
like image 199
Naffnuff Avatar answered Dec 30 '22 20:12

Naffnuff


Add inline to your definitions.

like image 37
justin Avatar answered Dec 30 '22 19:12

justin