I have such header file declaration
#ifndef CONTAINER_H
#define CONTAINER_H
#include <map>
#include "object.h"
namespace memory {
template<class T>
class Collector {
};
template <class T>
class Collector<T*> {
private:
std::map<std::string, T*> mem_storage;
public:
std::string put_object(T* to_store);
T* get_object(std::string key);
void list_objects();
void clean_up();
void clean_object(std::string key); // force cleaning
void mark_object_to_remove(std::string key); // mark unused
void clean_removable(); // move to scheduled task
~Collector() {};
};
}
#endif
And such implementation
#include "../headers/container.h"
#define null nullptr
#include <iostream>
#include <string>
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <boost/lexical_cast.hpp>
using namespace memory;
template<typename T>
void Collector<T*>::clean_object(std::string key) {
int removed = mem_storage.erase(key);
std::string removed_string = boost::lexical_cast<std::string>(removed);
}
template<typename T>
void Collector<T*>::clean_up() {
mem_storage.clear();
}
template<typename T>
T* Collector<T*>::get_object(std::string key) {
typename std::map<std::string, T*>::iterator it = mem_storage.find(key);
if (it != mem_storage.end()) {
return it->second;
} else {
return null;
}
}
template<typename T>
void Collector<T*>::list_objects() {
for (typename std::map<std::string, T*>::iterator it = mem_storage.begin(); it != mem_storage.end(); ++it) {
std::cout << it->first << " => " << it->second->to_string() << std::endl;
}
}
template<typename T>
std::string Collector<T*>::put_object(T* to_store) {
boost::uuids::uuid uuid = boost::uuids::random_generator()();
std::string key = boost::uuids::to_string(uuid);
mem_storage[key] = to_store;
return key;
}
template<typename T>
void Collector<T*>::mark_object_to_remove(std::string key) {
mem_storage.find(key)->second->removable = true;
}
template<typename T>
void Collector<T*>::clean_removable() {
typename std::map<std::string, T*>::iterator it = mem_storage.begin();
while (it != mem_storage.end()) {
std::string id = it->first;
T instance = it->second;
if (instance->removable) {
mem_storage.erase(it++);
} else {
++it;
}
}
}
My main file looks like this
#include <iostream>
#define byte unsigned char
#define null nullptr
#include "base/headers/object.h"
#include "base/headers/indexed_object.h"
#include "base/headers/container.h"
#include "base/headers/reflector.h"
void garbage_collection_prototype_example() {
core::Object *io = runtime::RuntimeReflector::create_instance(
"core::IndexedObject");
core::Object *io_new = runtime::RuntimeReflector::create_instance("core::IndexedObject");
std::cout << "Class 1 name: [" + io->get_class_name() + "]" << std::endl;
std::cout << "Class 2 name: [" + io_new->get_class_name() + "]" << std::endl;
bool is_same = io_new->is_same_instance(io);
std::cout << "Is same: " << std::boolalpha << is_same << std::endl;
memory::Collector<core::Object*> *garbage_collector = new memory::Collector<core::Object*>();
std::string key = garbage_collector->put_object(io);
std::string key_new = garbage_collector->put_object(io_new);
garbage_collector->list_objects();
garbage_collector->mark_object_to_remove(key);
garbage_collector->clean_removable();
garbage_collector->list_objects();
garbage_collector->clean_up();
}
int main(int argc, char *argv[]) {
garbage_collection_prototype_example();
return 0;
}
But I have bunch of errors when I try to build project
D:\c++\eclipse_workspace\SDLAttempts\Debug/../src/main.cpp:22: undefined reference to `memory::Collector<core::Object*>::put_object(core::Object*)'
D:\c++\eclipse_workspace\SDLAttempts\Debug/../src/main.cpp:23: undefined reference to `memory::Collector<core::Object*>::put_object(core::Object*)'
D:\c++\eclipse_workspace\SDLAttempts\Debug/../src/main.cpp:24: undefined reference to `memory::Collector<core::Object*>::list_objects()'
D:\c++\eclipse_workspace\SDLAttempts\Debug/../src/main.cpp:26: undefined reference to `memory::Collector<core::Object*>::mark_object_to_remove(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)'
D:\c++\eclipse_workspace\SDLAttempts\Debug/../src/main.cpp:27: undefined reference to `memory::Collector<core::Object*>::clean_removable()'
D:\c++\eclipse_workspace\SDLAttempts\Debug/../src/main.cpp:29: undefined reference to `memory::Collector<core::Object*>::list_objects()'
D:\c++\eclipse_workspace\SDLAttempts\Debug/../src/main.cpp:31: undefined reference to `memory::Collector<core::Object*>::clean_up()'
It's like it can't see any of methods of Collector.
Could it be due to my empty declaration of base non-specialized class Collector? But I tried to add there implementations of the methods and it did not help.
Short answer
You can't put the definition (implementation) of function templates into a .cpp file. Put them into the same header where they are declared.
Explanation
This is because function templates get instantiated when (where) they are used, so the compiler needs to see how they are defined in any place they are used. There are some workarounds to take definition separate from declaration of function/class templates, but all of them imply the inclusion of the definition of the template into the translation unit in which it is used.
Related
So lately I learnt how to copy a map's reference and the next step is that I want to store map references in a template class.
So in my main method my goal is to create map_storage where I store my maps and do different calculations on them.
Template class:
#include <map>
#include <compare>
#include <utility>
#ifndef MAPALIGN_H
#define MAPALIGN_H
template<class Key, class T, class Compare = std::less<Key> >
class map_storage
{
public:
map_storage() : size_(0), data_(new std::map<Key, T, Compare>[100]){}
void add(std::map<Key, T, Compare> &temp_map)
{
data_[size_] = temp_map;
size_ += 1;
}
void action(){
std::cout << std::endl;
std::cout << "Size on action " << size_ << std::endl;
for(int i=0; i<size_; i++){
print_map(data_[i]);
}
}
void print_map(std::map<Key, T, Compare> &temp_map){
std::cout << "Size of given map is : " << temp_map.size() << std::endl;
}
void modifyMaps(..){
....
}
private:
int size_;
std::map<Key, T, Compare>* data_;
};
#endif
When I modify the maps in data_ (add new key,values etc.) I want to see the changes in main too and the same thing for the other way around.
My question is that How can I store the references of the maps in a template class?
Example of the main method:
#include <iostream>
#include "mapalign.h"
#include <string>
#include <map>
int main()
{
std::map<std::string, int> map1;
maps_storage<std::stringm int> maps_storage;
maps_storage.add(map1);
map1["asd"] = 1;
map1["dsa"] = 2;
// This should print 1 and 2 but prints 1 and 0 because the map is not in the map_storage by reference.
map_storage.action();
return 0;
}
Output:
1
0
Expected Output:
1
2
This question already has answers here:
Why can templates only be implemented in the header file?
(17 answers)
Closed 2 years ago.
I am currently trying to implement a min heap, however, I am running into an issue.
I am having difficulty determining why my code is not linking properly when I compile it. The files, commands, and error are as follows:
min_heap.h
#ifndef MIN_HEAP_H
#define MIN_HEAP_H
#include <vector>
#include <algorithm>
using namespace std;
template <typename T>
struct HeapNode {
HeapNode(const T data, const int key) : data(data), key(key) {}
bool operator<(const HeapNode<T>& rhs) {return this->key < rhs.key;}
bool operator<=(const HeapNode<T>& rhs) {return this->key <= rhs.key;}
T data;
int key;
};
template <typename T>
class MinHeap {
public:
MinHeap() {};
~MinHeap() {};
void insert(const T data, const int key);
T extract_min();
T peek() const;
int size() const;
private:
vector<HeapNode<T>> heap;
};
#endif
min_heap.cpp
#include <iostream>
#include "min_heap.h"
using namespace std;
template <typename T>
void MinHeap<T>::insert(const T data, const int key){
cout << "Insertion." << endl;
if(this->heap.size() == 0){
cout << "adding first node" << endl;
this->heap.push_back(new HeapNode<T>(data, key));
}else{
cout << "not first node." << endl;
this->heap.push_back(new HeapNode<T>(data, key));
int currentIndex = this->heap.size() - 1;
while(((currentIndex - 1) / 2) >= 0){
if(this->heap[(currentIndex - 1) / 2] > this->heap[currentIndex]){
swap(this->heap[currentIndex], this->heap[(currentIndex - 1) / 2]);
currentIndex = ((currentIndex - 1) / 2);
}else{
break;
}
}
}
}
template <typename T>
T MinHeap<T>::extract_min(){
T data;
data = NULL;
return data;
}
template<typename T>
T MinHeap<T>::peek() const{
T data;
if(this->heap.size() == 0){
cout << "size: 0" << endl;
data = NULL;
}else{
cout << "something in there" << endl;
data = this->heap[0];
}
return data;
}
template<typename T>
int MinHeap<T>::size() const {
return this->heap.size();
};
customMain.cpp
#include <iostream>
#include "min_heap.h"
using namespace std;
int main(){
MinHeap<char> heap = MinHeap<char>();
const char data = 'a';
const int key = 7;
heap.insert(data, key);
return 0;
}
I am compiling the files above with the following
g++ -c min_heap.cpp
g++ -c customMain.cpp
g++ min_heap.o customMain.o
The error:
customMain.o:customMain.cpp:(.text+0x41): undefined reference to `MinHeap<char>::insert(char, int)'
collect2.exe: error: ld returned 1 exit status
Any help would be greatly appreciated!
Two options.
1) Move the template implementations out of the cpp file, and put them in the header. The compiler, when compiling main.cpp, cannot see the code it needs to generate the specialisation for char, so you end up being left with an unresolved external. Moving the code to the header will solve the issue (almost all template code resides in header files)
2) You can export specialisations from the cpp file, but then the types are locked down in advance (i.e. you can only use the exported types), which kinda reduces the flexibility and whole point of using templates. That's why 99.99% of people choose option 1, 99.99% of the time.
// export specialisations for char, short, int, and float
template class MinHeap<char>;
template class MinHeap<short>;
template class MinHeap<int>;
template class MinHeap<float>;
So I am creating a Stack class for an assignment in C++. The core of the assignment is to familiarize us with templates. I have read my book over and over and looked question after question on here.
I need to have my Stack class be able to be constructed by
Stack s2;
but I get an error when I compile my test.cpp and can only compile when i construct as
Stack<T> s1;
where T is a std::string, int, etc. How do build my Stack so I can use both constructors?
Stack.cpp
#include <iostream>
#include <vector>
#include <stdlib.h>
#include <string>
using namespace std;
template<typename T>
class Stack {
public:
Stack();
void Push(T val);
T Pop();
void Print();
private:
vector<T> vecT;
};
template <typename T>
Stack<T>::Stack() { }
template <typename T>
void Stack<T>::Push(T val) { vecT.push_back(val); }
template <typename T>
T Stack<T>::Pop() { vecT.pop_back(); }
template <typename T>
void Stack<T>::Print() {
cout << "[ ";
for(int i=0; i<vecT.size(); i++) {
cout << vecT[i] << " ";
}
cout << "]";
}
test.cpp
#include <iostream>
#include <vector>
#include <stdlib.h>
#include <string>
#include "Stack.cpp"
using namespace std;
int main() {
Stack<string> s1;
s1.Push("values1");
s1.Push("values2");
s1.Print();
Stack s2;
s2.Push("values1");
s2.Push("values2");
s2.Print();
}
What about a default template parameter?
template<typename T = std::string>
class Stack {
Anyway, with
Stack<T> s1;
and
Stack s2;
you're not using different constructors; you're using, in both cases, the same default (no arguments) constructor.
Instead of templating everything you could just use a stack of a variant type (since C++17, before use Boost.Variant). With the code below you can now push int, double, and std::string onto the stack. Furthermore, Pop() is missing a return statement. Also, pop_back() of vector returns nothing.
#include <iostream>
#include <vector>
#include <string>
#include <variant>
class Stack {
using Variant = std::variant<int,double,std::string>;
public:
Stack();
void Push(Variant val);
void Pop();
void Print();
private:
std::vector<Variant> vecT;
};
Stack::Stack() : vecT() {}
void Stack::Push(Variant val) { vecT.push_back(val); }
void Stack::Pop() { vecT.pop_back(); }
void Stack::Print() {
std::cout << "[ ";
for ( auto const& v : vecT )
std::visit([] (auto&& arg) { std::cout << arg << " "; }, v);
std::cout << "]\n";
}
int main() {
Stack s1;
s1.Push("values1");
s1.Push("values2");
s1.Print();
Stack s2;
s2.Push("values1");
s2.Push("values2");
s2.Print();
}
With Boost.Variant you get a C++98 compatible solution.
#include <iostream>
#include <vector>
#include <string>
#include <boost/variant.hpp>
class Stack {
typedef boost::variant<int,double,std::string> Variant;
typedef std::vector<Variant>::iterator Iterator;
std::vector<Variant> vecT;
struct visitor : public boost::static_visitor<void>
{
template < typename T >
void operator()(T const& arg) const { std::cout << arg << " "; }
};
public:
Stack();
void Push(Variant val);
void Pop();
void Print();
};
Stack::Stack() : vecT() {}
void Stack::Push(Variant val) { vecT.push_back(val); }
void Stack::Pop() { vecT.pop_back(); }
void Stack::Print() {
std::cout << "[ ";
for ( Iterator it = vecT.begin(); it != vecT.end(); ++it )
boost::apply_visitor( visitor(), *it );
std::cout << "]\n";
}
int main() {
Stack s1;
s1.Push("values1");
s1.Push("values2");
s1.Print();
Stack s2;
s2.Push("values1");
s2.Push("values2");
s2.Print();
}
You could use a default template parameter:
template<typename T = int>
class Stack {
Then you could construct using:
Stack<> s2;
Also, the constructors are identical, you aren't calling a different one each time, but rather the template argument differs.
driver.cc
#include <iostream>
#include "dynStack.h"
using namespace std;
// class definition
int main()
{
dynstack<double> it;
cout << "hello";
return 0;
}
dynStack.h
template <class T>
class dynstack {
public:
dynstack();
void push(T data);
private:
};
#include "dynStack.cc"
dynStack.cc
template <class T>
dynstack<T>::dynstack() { // <-- Here
}
template <class T> // // <-- And here
void dynstack<T>::push(T data)
{
}
I'm new to C++. The bolded lines are reported errors. The first one says "error: 'dynStack' does not name a type" The second one says "exrror: expected initializer before '<' token". I have spent hours on this and can't find the errors. Can anyone help? Thank you.
I was given a sample solution similar to this. Here is the sample:
main.cc
#include <iostream>
// #include the header file - as always
#include "temp.h"
using namespace std;
int main()
{
Thing<int> it(1);
Thing<double> dt(3.14);
cout << endl;
cout << "it = " << it.getData() << endl;
cout << endl;
cout << "dt = " << dt.getData() << endl;
cout << endl;
return 0;
}
temp.h
template <class T>
class Thing
{
private:
T data;
void setData(T data);
public:
Thing(T data);
T getData() const;
};
// MUST #include the implementation file here
#include "temp.cc"
temp.cc
// DO NOT #include the header file
template <class T>
Thing<T>::Thing(T data)
{
this->setData(data);
}
template <class T>
void Thing<T>::setData(T data)
{
this->data = data;
}
template <class T>
T Thing<T>::getData() const
{
return this->data;
}
It appears that you are trying to compile both driver.cc and dynStack.cc. The only file you compile with this setup is driver.cc.
Try this: Move contents of dynstack.cc entirely to dynstack.h and get rid of dynstack.cc
EDIT after reading comment responses:
If you want to keep dynstack.cc, its fine, just make sure you do not attempt to compile dynstack.cc I would name it a some other extension other than .cc which is conventionally for C++ implementation. Avoid .cc, .cpp, .cxx etc; use a uncommon extension such as .hc :-)
I'm trying to write a C++0x wrapper around dlopen()/dlsym() to dynamically load functions from shared objects:
class DynamicLoader
{
public:
DynamicLoader(std::string const& filename);
template<class Signature>
std::function<Signature> load(std::string const& functionName);
private:
void *itsLibraryHandle;
};
DynamicLoader::DynamicLoader(std::string const& filename)
{
itsLibraryHandle = dlopen(filename.c_str(), RTLD_LAZY);
if(!itsLibraryHandle)
{ /* Throw Some Error */ }
}
template<class Signature>
std::function<Signature> DynamicLoader::load(std::string const& functionName)
{
return <insert magic here> dlsym(itsHandle, functionName.c_str());
}
Is there a way to convert the void* function pointer returned by dlsym into a std::function?
try this:
static_cast<Signature*>()
seems works in VC10
complete test:
#include <functional>
void test()
{}
template <typename Signature>
std::function<Signature> cast(void* f)
{
return static_cast<Signature*>(f);
}
int main()
{
std::function<void()> f = cast<void()>(&test);
return 0;
}
Based on what I see here: http://pubs.opengroup.org/onlinepubs/009695399/functions/dlsym.html
#include <boost/function_types/components.hpp>
#include <boost/function_types/function_pointer.hpp>
template< typename Signature >
std::function<Signature> DynamicLoader::load(std::string const& name)
{
namespace ft = boost::function_types;
typedef typename ft::function_pointer< typename ft::components<Signature>::type >::type fp_t;
fp_t fun_ptr;
*reinterpret_cast<void**>(&fun_ptr) = dlsym(itsHandle, name.c_str());
return fun_ptr;
}
I've never used dlsym so I don't understand why the cast is being done that way and not simply casting dlsym's return like so:
fun_ptr = reinterpret_cast<fp_t>(dlsym(itsHandle, name.c_str());
You just need to cast result of dlsym() call to a proper type. Here's a complete working example:
#include <functional>
#include <iostream>
#include <stdexcept>
#include <string>
#include <dlfcn.h>
class DynamicLoader
{
public:
DynamicLoader(std::string const& filename) :
m_handle(dlopen(filename.c_str(), RTLD_LAZY))
{
if (!m_handle)
{
throw std::logic_error("can't load library named \"" + filename + "\"");
}
}
template<class T>
std::function<T> load(std::string const& functionName) const
{
dlerror();
void* const result = dlsym(m_handle, functionName.c_str());
if (!result)
{
char* const error = dlerror();
if (error)
{
throw std::logic_error("can't find symbol named \"" + functionName + "\": " + error);
}
}
return reinterpret_cast<T*>(result);
}
private:
void* const m_handle;
};
int main()
{
DynamicLoader const loader("/lib64/libz.so.1");
auto const zlibVersion = loader.load<char const* (void)>("zlibVersion");
std::cout << "zlib version: " << zlibVersion() << std::endl;
return 0;
}