Store maps in template class by reference - c++

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

Related

Get template parameter type of derived class in map that doesn't know about template parameters

I am trying to get the template parameter type in this scenario:
#include <iostream>
#include <string>
#include <map>
#include <typeinfo>
class Base {
public:
typedef char myType;
};
template <typename T>
class Derived : public Base {
public:
typedef T myType;
};
int main() {
std::map<std::string, Base*> myMap;
myMap["test1"] = new Derived<int>();
myMap["test2"] = new Derived<float>();
std::cout << typeid(myMap["test1"]).name() << std::endl; // prints Base
std::cout << typeid(myMap["test2"]).name() << std::endl; // prints Base
//myMap["test1"]->myType test; // invalid use of 'Base::myType'
std::cout << typeid(dynamic_cast<Derived*>(myMap["test1"])->myType).name() << std::endl; // invalid use of template-name 'Derived' without an argument list. Should print "int" ...
std::cout << typeid(dynamic_cast<Derived*>(myMap["test2"])->myType).name() << std::endl; // invalid use of template-name 'Derived' without an argument list. Should print "float" ...
}
The map holds elements of type Base and thus, also of type Derived with a template parameter. However on retrieving elements from the map I am not able to get the template parameter type back again. I tried to add a typedef to both classes but it doesn't work.
Do you have hints to resolve this problem?
Thanks in advance!
Do you have hints to resolve this problem?
type names don't work like virtual member functions. What you need is a virtual member function.
Here's a demonstrative program:
#include <iostream>
#include <string>
#include <map>
#include <typeinfo>
class Base {
public:
virtual std::type_info const& myType() const { return typeid(char); }
};
template <typename T>
class Derived : public Base {
public:
virtual std::type_info const& myType() const { return typeid(T); }
};
int main() {
std::map<std::string, Base*> myMap;
myMap["test1"] = new Derived<int>();
myMap["test2"] = new Derived<float>();
std::cout << myMap["test1"]->myType().name() << std::endl;
std::cout << myMap["test2"]->myType().name() << std::endl;
}
Output with g++:
i
f

C++ templated class default constructor fails

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.

Include counter in boost::fusion::for_each

For my current project it is necessary to iterate over a boost::fusion container. For this I found boost::fusion::for_each quite useful. Now it is necessary for me to increase a counter for each time the operator() of Functor got called.
Firstly, I tried to add a member int count to Functor, but this is not possible, as it is necessary that operator() must be a const Memberfunction of Functor.
The following short code snippet is an illustration of what I wanted to achieve.
#include <boost/fusion/container/vector.hpp>
#include <string>
#include <boost/fusion/include/for_each.hpp>
#include <iostream>
struct Functor {
template<typename T>
void operator()(T& t) const{
std::cout << typeid(t).name() << " -> " << t << std::endl;
// Desired Output:
// int -> 5 : 0
// std::string -> test : 1
// double -> 5.3 : 2
// double -> 10. : 3
}
};
int main(int argc, char**args) {
typedef boost::fusion::vector<int, std::string, double, double> TData;
TData v(5, std::string("test"), 5.3, 10.);
boost::fusion::for_each(v, Functor());
}
Is there a workaround for this kind of problem?

multiple errors undefined reference to method

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.

convert boost::fusion::set to boost::fusion::map by using boost::fusion::fold

I have a fusion set and would like to convert it into a fusion map.
#include <cstdlib>
#include <iostream>
#include <boost/fusion/include/fold.hpp>
#include <boost/fusion/include/map.hpp>
#include <boost/fusion/include/make_map.hpp>
#include <boost/fusion/include/push_back.hpp>
#include <boost/fusion/include/pair.hpp>
#include <boost/fusion/container/set.hpp>
#include <boost/fusion/include/for_each.hpp>
#include <boost/fusion/include/at_key.hpp>
struct node_base
{
int get() {return 123;}
};
struct node_a : public node_base
{
node_a(){std::cout << "node_a::Ctor"<< std::endl;}
};
struct node_b : public node_base
{
node_b(){std::cout << "node_b::Ctor"<< std::endl;}
};
struct node_c : public node_base
{
node_c(){std::cout << "node_c::Ctor"<< std::endl;}
};
typedef ::boost::fusion::set<node_a,node_b,node_c> my_fusion_set;
my_fusion_set my_set;
struct push_back_map
{
typedef ::boost::fusion::map<> result_type;
template<class NODE>
::boost::fusion::map<> operator() (const ::boost::fusion::map<> & m, const NODE & n)
{
return ::boost::fusion::push_back(
m
, ::boost::fusion::make_pair<NODE>(n)
);
}
};
::boost::fusion::map<> my_map =
::boost::fusion::fold(
my_set
, ::boost::fusion::map<>()
, push_back_map()
);
struct print_map
{
template<class P>
void operator()(P & p) const
{
std::cout << p.second.get() << std::endl;
}
};
int main()
{
::boost::fusion::for_each(my_map, print_map());
std::cout << ::boost::fusion::at_key<node_a>(my_set).get() << std::endl;
//std::cout << ::boost::fusion::at_key<node_a>(my_map).second.get() << std::endl;
system("pause");
return 0;
}
This code compiles. But it couldn't print out the results of my_map. my_set is constructed successfully. my_map is simply converted from a my_set with each element's original type as the key and its instance object from default constructor as the value. I am wondering if my_map is created successfully from fusion::fold. my_set is able to query by calling fusion::at_key<> on it, but my_map doesn't work for at_key<>.
Thanks!
I couldn't think of a concise way to create fusion::map from
fusion::set with fold.
So this answer won't resolve your question directly.
I post this in case this would give you some hint.
How about
preparing a type list(as my_types in the following), and
creating fusion::set and fusion::map from that list
instead of creating fusion::map from fusion::set directly?
For example:
namespace bf = boost::fusion;
namespace bm = boost::mpl;
typedef bm::vector<node_a,node_b,node_c> my_types;
typedef bm::fold<
my_types
, bm::vector<>
, bm::push_back< bm::_1, bf::pair< bm::_2, bm::_2 > >
>::type my_map_types;
int main() {
bf::result_of::as_set<my_types>::type my_set;
bf::result_of::as_map<my_map_types>::type my_map;
std::cout << bf::at_key<node_a>(my_set).get() << std::endl;
std::cout << bf::at_key<node_a>(my_map).get() << std::endl;
}
Here is a test on ideone.
Incidentally, if key-type and value-type in your fusion::map are always
identical, fusion::set will meet that purpose.