Compiling hybrid classes (templated and untemplated functions) into a static library - c++

I'm writing a game engine lib, for the sake of science. I've written static libs successfully in the past, although there were no templated functions.
When dealing with templated functions, I use to sepparate their code from the untemplated ones. Templated functions code lie in the header file, while the others in the .cpp/.hpp file.
Below is a snippet of one of it's modules: signals.
// Connection.h
#pragma once
#include <memory>
#include <functional>
namespace mqs
{
using Disconnector = std::function<void(std::uint32_t)>;
class Connection final
{
public:
explicit Connection(std::shared_ptr<mqs::Disconnector> disconnector, std::uint32_t index);
bool connected() const;
void disconnect() const;
private:
std::uint32_t index;
std::weak_ptr<mqs::Disconnector> disconnector;
};
}
// Signal.h
#pragma once
#include <vector>
#include "connection.hpp"
namespace mqs
{
template <typename...>
class Signal;
template <typename R, typename... A>
class Signal<R(A...)> final
{
public:
Signal();
template <typename Lambda>
mqs::Connection connect(Lambda&& lambda) {
slots.push_back(std::forward<Lambda>(lambda));
return mqs::Connection(disconnector, slots.size() - 1U);
}
void operator()(A&&... args) const;
unsigned connections() const;
private:
std::vector<std::function<R(A...)>> slots;
std::shared_ptr<mqs::Disconnector> disconnector;
};
}
// Connection.hpp
#pragma once
#include "connection.h"
namespace mqs
{
Connection::Connection(std::shared_ptr<mqs::Disconnector> disconnector, std::uint32_t index) {
this->index = index;
this->disconnector = disconnector;
}
bool Connection::connected() const {
return !disconnector.expired();
}
void Connection::disconnect() const {
if (const auto& lock = disconnector.lock()) {
lock->operator()(index);
}
}
}
// Signal.hpp
#pragma once
#include "signal.h"
namespace mqs
{
template <typename R, typename... A>
Signal<R(A...)>::Signal() {
disconnector = std::make_shared<mqs::Disconnector>([this](std::uint32_t index) {
slots.erase(slots.begin() + index);
});
}
template <typename R, typename... A>
void Signal<R(A...)>::operator()(A&&... args) const {
for (auto& slot : slots) {
slot(std::forward<A>(args)...);
}
}
template <typename R, typename... A>
unsigned Signal<R(A...)>::connections() const {
return slots.size();
}
}
It compiles and all, however one of the problems I've been dealing with, is that mqs::Signal (signal.hpp) cannot be included in different headers or it will cause a function already has a body. When including signal.h I get unresolved external symbol which makes sense.
I've also tried making inline all the functions defined in their .hpp files above.
Is there any way to achieve this other than using huge header-only approaches?

As you already figured out, you need to make the function templates inline. This is necessary because the templates first need to be instantiated to become compilable functions, and that means the compiler needs source code.
However, if you look at members like Signal<R(A...)>::disconnector;, you'll notice that they are not dependent on R or A.... Hence, you could move them to a non-template base class.
There's a fairly common convention to use the extension .ipp for implementation files that still need to be included, e.g. because they contain template code. These will typically be included by the corresponding .hpp file, just before the #endif of the header guard. Therefore an .ipp file doesn't need its own header guard.

Related

Understanding how imports are working in C++

I'm trying to understand how including works in C++. I have two questions about it. The first one is on how properly import the .h file. For example I created the following HashNode.h file:
namespace HashNode{
template<class Data>
class HashNode{
private:
Data data;
HashNode *next;
public:
explicit HashNode(const Data &data);
Data getKey();
~Node();
};
}
So in the HashNode.cpp file, it should like:
#include "HashNode.h"
using namespace HashNode;
template <class Data> // ~~~ HERE 1 ~~~
HashNode::HashNode(const Data &data) {//todo};
template <class Data> // ~~~ HERE 2 ~~~
Data* HashNode::getKey() {
//todo
}
HashNode::~Node() {
//todo
}
This way it works but do I have to include template <class Data> beside each function which uses Data? Why it does not recognize Data without including template <class Data>?
Also I have created the Hash.h file which should use the HashNode.h file:
#include "HashNode.h"
using namespace HashNode;
namespace Hash {
template <class Data>
class Hash {
typedef enum {
GOOD = 0,
BAD = -1,
BAD_ALLOC = -2
} Status;
private:
HashNode **hash;
int capacity;
int size;
public:
explicit Hash(int size);
Status insertData(const Data &data);
~Hash();
};
}
But I get the the following error: Can't resolve type 'HashNode'. Why it can't see the import?
In the Hash.cpp file I get Unused import statement for #include "HashNode.h". Why is that?
Also, what if I want to include private functions - should them be in the .h file or in the .cpp file?
The member functions of a template class are themselves also templates. Because of this, they need to be defined with any required template parameters and template type definitions.
About your second question, it has to do with namespaces. As I see it, having namespace and class under the same naming might cause you ambiguity. Although, everything seems to be fine on the structural side of the code. Try using #pragma once or some kind of guards to prevent this kind of issues.

extern declared template specialized function not found

i'm trying to implement a clone of the json serialization library nlohmann::json as a learning experience, and i'm having trouble with the interface for user defined (json<->User type) conversion.
Basically i want the user to be able to overload two function: to_json(json&, const Type&) and from_json(const json&, Type&). Then the library will use overload resolution to call theses function in the templated operator= and one argument constructor.
It works fine when i'm just defining theses function directly but when i try to make a template definition for multiple types (in this example the class S) the linker can't find the definition.
I've tried to explicitly instantiate the function for individual instances of the templated class although i would prefer avoiding having to do that in the final product.
I'm guessing it has to do with the fact that templated function don't have the same signature than free function, but i don't see what i can do to make it work. What am i missing ? I also couldn't find result on google so is it a documented pattern or an anti pattern ?
Thanks you. Below i tried to minimize my problem in one short example.
Class.hpp
#pragma once
#include <cstdio>
template<size_t i>
class S {
size_t n = i;
};
template<size_t i>
void g(const S<i>& s) {
printf("S<%u>\n", i);
}
Class.cpp
#include "Class.hpp"
template void g<10>(const S<10>&); // <-- Even with explicitly instanciation
void g(const bool& b) {
printf("%s\n", b ? "true" : "false");
}
main.cpp
#include "Class.hpp"
template<typename T>
void f(T t) {
extern void g(const T&);
g(t);
}
int main(int, char**) {
S<10> s;
//f(s); <-- linker error: void g(class S<10> const &) not found.
f(false);
}
The name lookup for g in g(t) call stops as soon as it finds extern void g(const T&); declaration; it never sees the declaration of the function template. So the compiler generates a call to a regular non-template function named g taking const S<10>&. But no such function is defined in your program - hence linker error.

C++: Templates not working from another class

When the following project is compiled, I get the following compiler error: (Visual Studio 2010)
1>usingclass.obj : error LNK2019: unresolved external symbol "public: static int __cdecl c1::arrSize(int * const)" (??$arrSize#H#c1##SAHQAH#Z) referenced in function "public: void __thiscall usingclass::a(void)" (?a#usingclass##QAEXXZ)
Code:
Headers:
c1.h
#pragma once
#include <array>
class c1
{
c1(void);
~c1(void);
public:
template<class T>
static int arrSize(T arr[]);
};
usingclass.h
#pragma once
#include "c1.h"
class usingclass
{
public:
usingclass(void);
~usingclass(void);
void a();
};
Source files:
c1.cpp
#include "c1.h"
c1::c1(void)
{
}
c1::~c1(void)
{
}
template <class T>
int c1::arrSize(T arr[])
{
return (sizeof(arr)/sizeof(arr[0]));
}
usingclass.cpp
#include "usingclass.h"
usingclass::usingclass(void)
{
}
usingclass::~usingclass(void)
{
}
void usingclass::a()
{
int a[2] = {1,2};
int b = c1::arrSize<int>(a);
}
How do I fix that?
Don't do this! The declaration is misleading.
template <class T>
int c1::arrSize(T arr[])
{
return (sizeof(arr)/sizeof(arr[0]));
}
is equivalent to
template <class T>
int c1::arrSize(T *arr)
{
return (sizeof(arr)/sizeof(arr[0]));
}
which will not give you want you want. The proper way to do it is like this:
class c1
{
c1(void);
~c1(void);
public:
template<class T,int N>
static int arrSize(T (&arr)[N]) { return N; }
};
arrSize takes a reference to an array as a parameter. The type of the array and the size of the array are template parameters and can be deduced by the compiler. The inline member function then just returns the size determined by the compiler.
You need to move
template <class T>
int c1::arrSize(T arr[])
{
return (sizeof(arr)/sizeof(arr[0]));
}
inside c1.h.
Template implementations must be visible to all translation units using that template (unless it's specialized, and in your case it's not).
This solves the compiler error but the underlying issue is solved with Vaughn Cato's answer. I missed that. You'll still need the definition in the header.
I think you have to define your template in c1.h itself. Because when you are including c1.h in your usingclass.h, and try to use template it does not find the expansion for template.
Or If you want to go with implementation of template in c1.cpp, then you have to include c1.cpp as well in usingclass.h.

failing to invoke template class, c++

I've defined a template class like so (providing .hpp file):
#ifndef PERSOANLVEC_H_
#define PERSOANLVEC_H_
#include <vector>
using namespace std;
template<class T, class PrnT> class PersoanlVec {
public:
PersoanlVec();
~PersoanlVec();
void push_back(T t);
void erase(int index);
PersoanlVec& operator[](int index);
const PersoanlVec& operator[](int index) const;
void print() const;
size_t size();
private:
vector<T> _vector;
};
#endif /* PERSOANLVEC_H_ */
Now, everything compiles ok with this class. When I try to use it I get
undefined reference to PersoanlVec<Person, Person>::PersoanlVec()'.
Here's where I call it:
#include "Person.h"
#include "PersoanlVec.hpp"
#include <cstdlib>
int main(void)
{
Person p1("yotam");
Person p2("yaara");
PersoanlVec<Person,Person> *a = new PersoanlVec<Person,Person>(); //<---ERROR HERE
return EXIT_SUCCESS;
}
This is my first try with templates, its not very clear for me obviously. I DO have a constructor with no parameters, Any ideas?
Thanks!
Do you have the content of your constructor and functions in a .cpp file? If yes, there's your problem. Put them in the header file, possible just inline in the class itself:
template<class T, class PrnT> class PersoanlVec {
public:
PersoanlVec(){
// code here...
}
~PersoanlVec(){
// code here...
}
void push_back(T t){
// code here...
}
void erase(int index){
// code here...
}
PersoanlVec& operator[](int index){
// code here...
}
const PersoanlVec& operator[](int index) const{
// code here...
}
void print() const{
// code here...
}
size_t size(){
// code here...
}
private:
vector<T> _vector;
};
For the reason, have a look here.
I am pretty sure you just forgot to add the file to the compilation process. Be careful with your misspelling, since that can cause generic pain.
Whenever you have different compilation units (classes for example, each with their .h/.cpp), your classes need to know of the interfaces, reason for which you normally include the header files, yet the compiler also needs to know the implementations so that it can bind together your binary file.
As such, you will need to call the compiler passing all the .cpp files in your project to it, otherwise it will fail letting you know you are referencing unimplemented pieces.
You need to have all of your template function definitions held in the header file rather than the CPP file - this is basically because the template definition will be used multiple times to create multiple types depending on what parameters you pass in to it as type parameters around your code. The only template related functions that should ever be defined in the CPP file are template specialization functions - those where you want to explicitly say (if user passed in type A and B then do this specifically instead of the default action).

Understanding template classes in c++ - problem with new-operator

Dear all, I've been stuck with this problem now for a few days and my searches were not successful.
What I am trying to do:
I want a template reader class (VariableReader) to handle different types of variables (usually unsigned int and pointers to vector).
I started with
#ifndef READER_H_
#define READER_H_
#include <string>
namespace BAT {
template <typename variableType = unsigned int>
class VariableReader {
public:
VariableReader<variableType>();
VariableReader<variableType>(std::string varName);
virtual ~VariableReader<variableType>();
std::string getVariableName();
void setVariableName(std::string varName);
bool isValidVariableName(std::string varName);
variableType getVariable();
private:
std::string variableName;
variableType variable;
};
}
#endif
and
#include "../../interface/Readers/VariableReader.h"
namespace BAT {
template<typename variableType>
VariableReader<variableType>::VariableReader() :
variableName("") {
// TODO Auto-generated constructor stub
}
template <typename variableType>
VariableReader<variableType>::VariableReader(std::string varName) :
variableName(varName) {
}
template <typename variableType>
std::string VariableReader<variableType>::getVariableName() {
return variableName;
}
template <typename variableType>
void VariableReader<variableType>::setVariableName(std::string varName) {
if (VariableReader::isValidVariableName(varName)) {
variableName = varName;
}
}
template <typename variableType>
bool VariableReader<variableType>::isValidVariableName(std::string varName) {
return varName != "";
}
template <typename variableType>
VariableReader<variableType>::~VariableReader() {
// TODO Auto-generated destructor stub
}
}
However, although it seems to compile I can't use it within other projects.
EDIT: forgot to post test-code:
#include "cute.h"
#include "ide_listener.h"
#include "cute_runner.h"
#include "Readers/VariableReader.h"
using namespace BAT;
static VariableReader<int> *reader;
void setUp(){
reader = new VariableReader<int>::VariableReader();//this is problem-line
}
void thisIsATest() {
ASSERTM("start writing tests", false);
}
void runSuite(){
cute::suite s;
//TODO add your test here
s.push_back(CUTE(thisIsATest));
cute::ide_listener lis;
cute::makeRunner(lis)(s, "The Suite");
}
int main(){
runSuite();
}
I get following error message:
Building target: BAT_Tests
Invoking: GCC C++ Linker
g++ -L"/workspace/BAT/Debug Gcov" -fprofile-arcs -ftest-coverage -std=c99 -o"BAT_Tests" ./src/Test.o -lBAT
./src/Test.o: In function `setUp()':
/workspace/BAT_Tests/Debug Gcov/../src/Test.cpp:13: undefined reference to `BAT::VariableReader<int>::VariableReader()'
collect2: ld returned 1 exit status
make: *** [BAT_Tests] Error 1
As I understand it the linker tries to find the constructor for VariableReader, which is not explicitly defined since I want to have a general constructor only.
Please help me to understand what I am missing.
The C++ FAQ Lite section on How can I avoid linker errors with my template functions? shows two solutions:
Move the template class's methods into the .h file (or a file included by the .h file).
Instantiate the template in the .cpp file using template VariableReader<unsigned int>;.
The constructor(s) and destructor doesn't need the template arguments in it. In addition, template classes must have the full source available to compile- you can't declare the members and define them in another translation unit like you can with normal classes.