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?
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
}
Add inline
to your definitions.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With