Initializing a std::vector in a template class - c++

I get linker errors when compiling the following code:
Here the header file:
// Solver.h
#ifndef SOLVER_H_
#define SOLVER_H_
#include <vector>
#include "Resource.h"
#include "ValueFunction.h"
template<typename T>
class Solver {
public:
Solver(std::vector<Resource>& resources);
private:
std::vector<T> valfuncs;
};
#endif /* SOLVER_H_ */
And here the source file:
// Solver.cpp
#include "Solver.h"
template<typename T>
Solver<T>::Solver(std::vector<Resource>& resources) :
valfuncs(resources.size()) {}
// Explicit class declaration
template class Solver<ValueFunction>;
And the call:
// openadp.cpp
#include "Solver.h"
int main(int argc, char *argv[]) {
std::vector<Resource> resources(4);
Solver<ValueFunction> sol(resources);
return 0;
}
The code is compiling fine if I remove valfuncs(resources.size()) from the initialization list. Why is it not possible to initialize the vector with the class passed from my template list?
Thanks in advance,
Reza
Update
Sorry, but this mini-example does not reproduce the error!
I'm trying to find one which does.
Update 2
The linker error was due to a wrong order of includes in my cmake files.
Remark
This question is not a duplicate of Why can templates only be implemented in the header file? first, because (the most obvious) the code compiles and second, there is an implicite instantiation of the Solver template: template class Solver<ValueFunction>;, thus the compiler is aware of an instance of the defined type.

Related

Multiple instances of Singleton in a c++ shared library on Windows

If I access a singleton in an executable that is linked a shared library and then access the same singleton within the shared library, two instances are created.
Is this not allowed or am I doing something wrong?
Here is some code to illustrate. I create a shared library containing Singleton.h, Manager.h, Window.h, Window.cpp, and DeclSpec.h
Singleton template class:
#ifndef SINGLETON_H
#define SINGLETON_H
#include <memory>
#include <iostream>
template <typename T>
class Singleton
{
private:
static std::unique_ptr<T> s_instance;
public:
static T &instance()
{
if (!s_instance) {
std::cout << "Creating..." << std::endl;
s_instance = std::make_unique<T>();
}
return *s_instance;
}
};
template <typename T>
std::unique_ptr<T> Singleton<T>::s_instance;
#endif //SINGLETON_H
I create a manager class that is a singleton:
#ifndef MANAGER_H
#define MANAGER_H
#include "Singleton.h"
class Manager : public Singleton<Manager>
{
};
#endif //MANAGER_H
Then I have a window class within the same library that uses the manager.
Here is the h (DECLSPEC is defined in DeclSpec.h and handles library export/import):
#ifndef WINDOW_H
#define WINDOW_H
#include "DeclSpec.h"
class DECLSPEC Window
{
public:
Window();
};
#endif //WINDOW_H
Here is the cpp:
#include "Manager.h"
#include "Window.h"
Window::Window()
{
Manager &m = Manager::instance();
}
Finally, I create an executable that is linked to the above shared library with a simple main.cpp:
#include "Manager.h"
#include "Window.h"
int main(void)
{
Manager &m = Manager::instance();
Window w;
return 0;
}
The output:
Creating...
Creating...
The singleton is created twice.
Any pointers?
Just like with any other class, you need to tell the compiler to either export or import the template class specialization. Here is how to properly declare your template specialization as import or export.
#ifndef MANAGER_H
#define MANAGER_H
#include "Singleton.h"
// <BEGIN modifications>
// forward declare the Manager class
class Manager;
// delcare your specialization as import or export
// NOTE: this only works on win32.
// For other platforms you would use the 'extern' keyword, and it does not go in the same place as DECLSPEC
template class DECLSPEC Singleton<Manager>;
// <END modifications>
class Manager : public Singleton<Manager>
{
};
#endif //MANAGER_H
The goal here is to tag the specific specialization of Singleton with DECLSPEC. We dont want to tag the template class itself because you may want to have singletons of other types that dont live in your main executable.
The microsoft compiler will not implement the templates tagged with a declspec. Other compilers (clang, gcc) use the 'extern' keyword for this purpose. So you are responsible for explicitly instantiating the template in one of your cpp files. If you forget to do this, you will get linker errors just like you had made a class member function and forgot to implement it.
Conversely, if you forget to reference Singleton somewhere in your code without the DECLSPEC (like if you forward declare the class), you will get 'multiple symbols defined' linker errors.
So add the following to your Manager.cpp to force the compiler to fully instantiate the template specialization of your Singleton:
template class Singleton<Manager>;

templates - undefined reference error

I have the following simple template code:
#ifndef CLUSTER_H
#define CLUSTER_H
#include <iostream>
#include <vector>
template <typename T, size_t K>
class Cluster
{
public:
void Print() const;
private:
std::vector<T> objects;
};
template <typename T, size_t K>
void Cluster<T,K>::Print() const
{
for (int i=0; i<objects.size(); i++)
{
T curr=objects[i];
std::cout << curr << " ";
}
std::cout << std::endl;
}
#endif
and for some reason I get the following error: "undefined reference to 'Cluster<int, 5u>::Print() const'. What could be the cause for this?
Thanks!
So, I'm going to go out on a limb here and say that you've defined a template function in a CPP file, which means it will end up in a different translation unit. Here's a simple example:
A header, example.h
#ifndef EXAMPLE_H
#define EXAMPLE_H
template<int TValue>
class example
{
public:
int get_tvalue();
};
#endif
A source file, example.cpp
#include "example.h"
template<int TValue>
int example<TValue>::get_tvalue()
{
return TValue;
}
And another source file, main.cpp
#include "example.h"
int main()
{
example<5> instance;
instance.get_tvalue();
return 0;
}
If I compile these together using GCC, I get undefined reference to 'example<5>::get_tvalue()'. This is because of the way template classes are instantiated. A template class definition is just that... a template, not an actual class. The actual class definition is created when a parameterised (or specifically, fully specialised) definition of that class occurs, in this case, example<5>. That fully specialised class definition only exists in main.cpp... there's no such class inside example.cpp! Example.cpp contains only the template, and no specialisations. This means the function, get_tvalue is not defined for example<5> in main.cpp, hence the error.
You can fix this in one of two ways. The first way is to always have your entire template class defined in its header file. This is the way its done with STL containers, for example. The alternative is to force creation of a parameterised class in example.cpp... you can do this by adding
template class example<5>;
to the end of example.cpp. Because there's now an actual class definition for example<5> in example.cpp, you will also get an actual function definition for example<5>::get_tvalue and when your translation units main.o and example.o are linked together at the end of the compilation step everything will be fine.
Obviously, this would be a poor approach in most cases, but under circumstances where your template parameters take only a small range of values it can work. Putting your whole class in the header file is probably easiest, safest and most flexible though.

"expected class-name before '{' token" when compiling

I've looked a lot, tried a bunch of things I found on stackoverflow and other websites but I still can't figure out how to fix this...
Main C++ file : source.cpp
#include "sources.h"
#include "fft_windows.h"
...
source.h
#include "Array2D.h"
...
Array2D.h
#ifndef ARRAY2D_H_
#define ARRAY2D_H_
#include <cassert>
#include "Features.h"
template <class T> class Array2D{
...
};
#endif
Features.h
#ifndef FEATURES_H_
#define FEATURES_H_
#include <string>
using namespace std ;
class Features : public Array2D {
...
};
#endif
fft_window.h (function declarations that are defined in fft_window.cpp)
//#include "Array2D.h"
template <class T>class Array2D;
void random_example(unsigned int i, Array2D <double> &arr);
...
I've tried everything I found and for some reason I still get this error in Features.h on the class Features : public Array2D { line...
Any idea ?
Array2D is not a class. Array2D<int> is, or Array2D<float>
First, Features.h needs to include Array2D.h, second you get an infinite include recursion with that, and third you need to specify the template parameter when inheriting from Array2D:
class Features : public Array2D<int> {
// example --- ^^^
Array2D is declared in Array2D.h file and you are using that template class in Features.h without #include"Array2D.h". Include that file as said and also remove the inclusion of Feature.h to make sure that there is no recursive includes.
Also, Array2D should be used with template argument, like <int>, <char>, <T> and so on.

Compile error when trying to register observer to template subject class

I am trying to implement an observer pattern with a template subject class. The observers don't (need to) know the subjects type, so I made an interface for the attach method without this type. This is my implementation:
SubjectInterface.h
#ifndef SUBJECTINTERFACE_H_
#define SUBJECTINTERFACE_H_
#include <list>
#include "Observer.h"
// Template-independant interface for registering observers
class SubjectInterface
{
public:
virtual void Attach(Observer*) = 0;
}; // class SubjectInterface
#endif // SUBJECTINTERFACE_H_
Subject.h
#ifndef SUBJECT_H_
#define SUBJECT_H_
#include <list>
#include "Observer.h"
#include "SubjectInterface.h"
template <class T>
class Subject : public SubjectInterface
{
public:
Subject();
~Subject();
void Attach(Observer*);
private:
T mValue;
std::list<Observer*> mObservers;
}; // class Subject
#include "Subject.cpp"
#endif // SUBJECT_H_
Subject.cpp
template <class T>
Subject<T>::Subject()
{
}
template <class T>
Subject<T>::~Subject()
{
}
template <class T>
void Subject<T>::Attach(Observer* test)
{
mObservers.push_back(test);
}
Observer.h
#ifndef OBSERVER_H_
#define OBSERVER_H_
#include "SubjectInterface.h"
#include <iostream>
class Observer
{
public:
Observer(SubjectInterface* Master);
virtual ~Observer();
private:
SubjectInterface* mMaster;
}; // class Observer
#endif // OBSERVER_H_
Observer.cpp
#include "Observer.h" // include header file
Observer::Observer(SubjectInterface* Master)
{
Master->Attach(this);
}
Observer::~Observer()
{
}
When I compile this using the gcc 4.3.4, I get the following error message:
SubjectInterface.h:10: error: ‘Observer’ has not been declared
I don't understand this, because the Observer is included just a few lines above. When I change the pointer type from Observer* to int*, it compiles OK. I assume that there is a problem with the template subject and the non-template interface to it, but that is not what gcc is telling me and that doesn't seem to be the problem when using int*.
I searched for template/observer, but what I found (e.g. Implementing a Subject/Observer pattern with templates) is not quite what I need.
Can anyone tell me, what I did wrong or how I can call the templated attach-method from a non-template observer?
You have a circular include chain, SubjectInterface.h includes Observer.h which in turns includes SubjectInterface.h.
This means that the include guards will prevent Observer from being visible. To fix it instead forward declare Observer.
// SubjectInterface.h
#ifndef SUBJECTINTERFACE_H_
#define SUBJECTINTERFACE_H_
#include <list>
class Observer; //Forward declaration
// Template-independant interface for registering observers
class SubjectInterface
{
public:
virtual void Attach(Observer*) = 0;
}; // class SubjectInterface
#endif // SUBJECTINTERFACE_H_
You have a circular dependency; Observer.h includes SubjectInterface.h, and vice versa. You will need to break this with a forward declaration.

Failure to compile incomplete types; circular dependencies

Hi am having problems compiling some code, I have a situation where A depends and B depends on A. I have put forward declarations but I keep getting the problems.
In file included from src/MemoWriteContext.h:7:0,
from src/MemoWriteContext.cpp:1:
src/MemoContext.h:29:20: error: field ‘memoWriteContext’ has incomplete type
MemoContext.h
#ifndef MEMOCONTEXT_H_
#define MEMOCONTEXT_H_
#include "sqlite/SqliteDb.h"
#include "Context.h"
#include "MemoWriteContext.h"
#include <string>
#include <memory>
#include <map>
namespace bbs
{
class MemoWriteContext;
class MemoContext : public Context
{
public:
//'structors
MemoContext(const std::map<std::string, std::shared_ptr<Context> > &_contexts,
sqlitecpp::SqliteDb &_sqliteDb);
~MemoContext();
protected:
//when called write the data back to the user
void performAction(const std::string &data, std::shared_ptr<UserAgent> agent);
private:
MemoWriteContext memoWriteContext;
}; //class memocontext
}
#endif // MEMOCONTEXT_H_
MemoWriteContext.h
#ifndef MEMOWRITECONTEXT_H_
#define MEMOWRITECONTEXT_H_
#include "Context.h"
#include "sqlite/SqliteDb.h"
#include "sqlite/PreparedStmt.h"
#include "MemoContext.h"
#include <string>
#include <memory>
#include <map>
namespace bbs
{
class MemoContext; //forward decl
class MemoWriteContext : public Context
{
public:
//'structors
MemoWriteContext(const std::map<std::string, std::shared_ptr<Context> > &_contexts,
MemoContext &_memoContext, sqlitecpp::SqliteDb &_sqliteDb);
~MemoWriteContext();
protected:
//when called write the data back to the user
virtual void performAction(const std::string &data, std::shared_ptr<UserAgent> agent);
virtual void onReceiveUserAgent(std::shared_ptr<UserAgent> agent);
private:
MemoContext &memoContext; //parent;
sqlitecpp::SqliteDb &sqliteDb;
sqlitecpp::PreparedStmt writeMemoStmt;
sqlitecpp::PreparedStmt findAgentIdStmt;
};
enum class MemoWriteState : char
{
USERNAME=0,
MESSAGE,
CONFIRM
};
class MemoWriteAgentData : public ContextAgentData
{
public:
MemoWriteState state;
int userId;
std::string message;
}; //class Memo Write Agent data
}
#endif // MEMOWRITECONTEXT_H_
Full source here.
I think your only problem is that MemoWriteContext.h has #include "MemoContext.h". The context only requires a reference which can use the forward declaration. But if you happen to include MemoWriteContext.h first it will then bring in MemoContext.h before it actually declares class MemoWriteContext. That will then use the forward declaration of class MemoWriteContext and fail. You can even see the ordering in your error message.
Just remove that #include or at least reverse the order of the includes in MemoWriteContext.cpp (since each .h including the other effectively reverses them back).
This:
class MemoWriteContext;
Is a forward declaration. It's an "incomplete type", and therefore cannot be instantiated.
The reason is that a C++ compiler must know the size of any type that has to be instantiated. Incomplete types have no size.
By the way, you can do this:
MemoWriteContext * ptr;
Because you actually declare a pointer, and pointers have a known size.
If you want to avoid dynamic allocations, then you'll have to fully declare the type by including MemoWriteContext.h and removing the forward declaration.