I have a class with a vector I'd like to fill with one of two types of class, selected by the user. Let's call my classes option1 and option2
What I'd like to do it something like
class storage_class
{
public:
storage_class(int sel, int n)
{
if(sel == 1)
for(int i = 0; i < n; i++)
my_store.push_back(std::make_unique<option1>());
else if(sel == 2)
for(int i = 0; i < n; i++)
my_store.push_back(std::make_unique<option2>());
}
private:
// Something like this but that actually works
std::vector<T> my_store;
};
Then I'd like to use it like this, or something similar, so there's no need to modify this usage dependent on the option chosen.
int main()
{
storage_class store(1);
int n_iterations = 4;
for(int i = 0; i < n_iterations; i++)
{
store.my_store[i]->create_data();
}
}
The classes option1 and option2 will be mathematical simulations that will be creating data and themselves store this data in a vector that are members of the class.
I want to store multiple instances of either option in a vector and then manipulate them from there. I can use C++17.
As you have c++17 in use, you can simply use a std::variant as type for the container which itself can keep all types you want to have.
Example:
class A { public: void Do() { std::cout << "A::Do" << std::endl; } };
class B { public: void Go() { std::cout << "B::Go" << std::endl; } };
template<class... Ts> struct funcs : Ts... { using Ts::operator()...; };
template<class... Ts> funcs(Ts...) -> funcs<Ts...>;
int main()
{
std::vector<std::variant<A,B>> vec;
vec.push_back(A{});
vec.push_back(B{});
for ( auto& el: vec)
{
std::visit( funcs{ [](A& a){ a.Do(); }, [](B& b) { b.Go(); } }, el);
}
}
Output:
A::Do
B::Go
The classes are fully independent and the methods can be simply called with std::visit and passing a callable object herein. I provide a simple funcs implementation, which simply collect all callable entities to simplify to interface the call to different methods of different unrelated classes here.
As std::variant is some kind of a tagged union, it needs the storage for the biggest type you have in use. If this wastes to much memory, you can store a pointer to the instance instead, maybe with std::unique_ptr or std::shared_ptr if you like some assistance for memory management ;)
Here is an example that tries to stay as close to your example as it can using a template parameter on class storage_class. See working version here. I've added only option1 and made the member my_store public as you access it in your main function.
#include <memory>
#include <vector>
#include <iostream>
struct option1{
void create_data(){ std::cout << "created\n"; }
};
template<typename T>
class storage_class
{
public:
storage_class(int n)
{
for(int i = 0; i < n; i++)
my_store.push_back(std::make_unique<T>());
}
std::vector<std::unique_ptr<T>> my_store;
};
int main()
{
storage_class<option1> store(4);
int n_iterations = 4;
for(int i = 0; i < n_iterations; i++)
{
store.my_store[i]->create_data();
}
}
another option would be to use std::variant. See workign version here.
#include <memory>
#include <vector>
#include <variant>
#include <iostream>
struct option1{
void create_data(){ std::cout << "created 1\n"; }
};
struct option2{
void create_data(){ std::cout << "created 2\n"; }
};
class storage_class
{
public:
using option = std::variant<std::unique_ptr<option1>,std::unique_ptr<option2>>;
storage_class(int sel, int n)
{
if(sel == 0)
for(int i = 0; i < n; i++)
my_store.push_back(option(std::make_unique<option1>()));
else if(sel == 1)
for(int i = 0; i < n; i++)
my_store.push_back(option(std::make_unique<option2>()));
}
std::vector<option> my_store;
};
int main()
{
storage_class store(1, 4);
int n_iterations = 4;
for(int i = 0; i < n_iterations; i++)
{
std::get<1>(store.my_store[i])->create_data();
}
}
Standard way is to make option1 and option2 derived classes from a base_class which seems consistent with your sample main(). Using a generic Factory class template, here is an example:
#include <functional>
#include <iostream>
#include <memory>
#include <unordered_map>
#include <vector>
// Generic Factory class template
template<typename K,typename T,typename... Ts>
class Factory
{
using Map = std::unordered_map<K, std::function<std::unique_ptr<T>(Ts...)>>;
const Map mMap;
public:
Factory(Map&& map):mMap(std::move(map)) { }
std::unique_ptr<T> operator()(const K& key, Ts... args) const
{
const typename Map::const_iterator itr = mMap.find(key);
return itr == mMap.cend() ? nullptr : itr->second(std::forward<Ts>(args)...);
}
};
class base_class
{
public:
virtual void create_data() = 0;
};
class option1 : public base_class
{
public:
void create_data() override
{
std::cout << "I'm option1." << std::endl;
}
};
class option2 : public base_class
{
public:
void create_data() override
{
std::cout << "I'm option2." << std::endl;
}
};
class storage_class
{
using SimulationFactory = Factory<int,base_class>; // Optionally add constructor parameter types
const SimulationFactory simulation_factory; // This can be made static const.
public:
storage_class(int sel, int n)
: simulation_factory(
{ { 1, []() { return std::make_unique<option1>(); } }
, { 2, []() { return std::make_unique<option2>(); } }
})
{
for (int i = 0; i < n; i++)
my_store.push_back(simulation_factory(sel));
}
std::vector<std::unique_ptr<base_class>> my_store;
};
int main()
{
int n_iterations = 4;
storage_class store(1, n_iterations);
for(int i = 0; i < n_iterations; i++)
{
store.my_store[i]->create_data();
}
}
This compiled for me on linux using g++ -std=c++17 main.cc.
There are improvements that can be made to this code, but I copied your main() function in order to illustrate the basic idea(s). Hope that helps.
Edit 21 Sept 2018 - Example of how to pass parameters into constructors.
File: factory.h
#pragma once
#include <functional>
#include <memory>
#include <unordered_map>
// Generic Factory class template
template<typename K,typename T,typename... Ts>
class Factory
{
using Map = std::unordered_map<K, std::function<std::unique_ptr<T>(Ts...)>>;
const Map mMap;
public:
Factory(Map&& map):mMap(std::move(map)) { }
std::unique_ptr<T> operator()(const K& key, Ts... args) const
{
const typename Map::const_iterator itr = mMap.find(key);
return itr == mMap.cend() ? nullptr : itr->second(std::forward<Ts>(args)...);
}
};
File: main.cc
#include "factory.h"
#include <iostream>
#include <string>
#include <vector>
class base_class
{
public:
virtual void create_data() = 0;
};
class option1 : public base_class
{
const double mD;
public:
option1(double d)
: mD(d)
{ }
void create_data() override
{
std::cout << "I'm option1: mD("<<mD<<')' << std::endl;
}
};
class option2 : public base_class
{
const double mD;
public:
option2(double d)
: mD(d)
{ }
void create_data() override
{
std::cout << "I'm option2: mD("<<mD<<')' << std::endl;
}
};
class storage_class
{
using SimulationFactory = Factory<int,base_class,double>; // Optionally add constructor parameter types
const SimulationFactory simulation_factory; // This can be made static const.
public:
storage_class(int sel, int n)
: simulation_factory(
{ { 1, [](double d) { return std::make_unique<option1>(d); } }
, { 2, [](double d) { return std::make_unique<option2>(d); } }
})
{
for (int i = 0; i < n; i++)
my_store.push_back(simulation_factory(sel,static_cast<double>(i)));
}
std::vector<std::unique_ptr<base_class>> my_store;
};
int main()
{
int n_iterations = 4;
storage_class store1(1, n_iterations);
storage_class store2(2, n_iterations);
for(int i = 0; i < n_iterations; i++)
{
store1.my_store[i]->create_data();
store2.my_store[i]->create_data();
}
}
Output:
I'm option1: mD(0)
I'm option2: mD(0)
I'm option1: mD(1)
I'm option2: mD(1)
I'm option1: mD(2)
I'm option2: mD(2)
I'm option1: mD(3)
I'm option2: mD(3)
Related
Let's say we have the following code in c-style
class Dog {
public:
void woof() {};
};
int main() {
Dog* mat[5][5];
mat[0][0] = new Dog();
mat[0][0]->woof();
}
How would you write it in cpp style using smart pointers? is the following is fine?
class Dog {
public:
void woof() {};
};
int main() {
std::unique_ptr<Dog> mat[5][5];
mat[0][0] = std::make_unique<Dog>();
mat[0][0]->woof();
}
or maybe even something such as:
class Dog {
public:
void woof() {};
};
int main() {
std::unique_ptr<std::unique_ptr<std::unique_ptr<Dog>[]>[]> mat = std::make_unique<std::unique_ptr<std::unique_ptr<Dog>[]>[]>(5);
for (int i = 0; i < 5; i++)
mat[i] = std::make_unique<std::unique_ptr<Dog>[]>(5);
mat[0][0] = std::make_unique<Dog>();
mat[0][0]->woof();
}
how can I do it in the most elegant and memory-efficient way?
If the dimensions are fixed, which I think they are, then you can use std::array. Then loop through and fill the elements with std::generate:
#include <iostream>
#include <array>
#include <algorithm>
#include <memory>
class Dog {
public:
void woof() { std::cout << "woof" << std::endl; };
};
int main() {
std::array<std::array<std::unique_ptr<Dog>, 5>, 5> matrix;
for (int x=0; x < 5; ++x)
{
std::generate(std::begin(matrix[x]), std::end(matrix[x]),
[]{ return std::make_unique<Dog>(); } );
}
matrix[0][0]->woof();
matrix[4][4]->woof();
return 0;
}
Demo
I have a piece of c++ code:
#include <iostream>
#include <string>
#include <map>
static counter = 0;
class Probe
{
private:
int supply_;
Probe(const Probe&);
public:
Probe()
{
supply_ = 10000;
}
int get_supply()
{
return supply_;
}
};
/********************************************************************************
template<class T> T Create(int counter, T& produced)
{
produced[counter] = new ; // ??????????????????????????????????????
return produced;
}
************************************************************************************/
std::map<int, Probe*> CreatInitWorkers(int counter, std::map<int, Probe*> &init_workers)
{
init_workers[counter] = new Probe();
return init_workers;
}
int main()
{
std::map<int, Probe*> workers;
for (int i = 0; i < 12; i++)
{
workers = CreatInitWorkers(worker_counter++, workers);
}
for (auto it : workers)
{
std::cout << it.first << std::endl;
}
}
I want to create a template function (as it shows between the stars) like the CreatInitWorkers function. But I don't know how to transfer the Probe class to the new operation because for my program there are still other classes needed to be there. Is there any way can do it? Thanks.
Something along these lines:
template<class T> T Create(int counter, T& produced)
{
using C = std::remove_pointer_t<std::decay_t<decltype(produced[counter])>>;
produced[counter] = new C();
return produced;
}
Demo
Error
e/c++/v1/algorithm:642:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/utility:321:9: error:
field type 'Space' is an abstract class
_T2 second;
^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/map:624:16: note:
Question
How can I define a std::vector of type Space which is an abstract class and then fill this vector with instances of the derived classes Empty, Snake, Ladder.
Context
I know abstract classes in C++ can not be instantiated. Instead I've read in several posts on this and other sites that you can create a collection of an abstract type if it the type is defined as a star * pointer or any of the <memory> managed pointer data types like std::unqiue_ptr<T>. I've tried to used shared_ptr<Space> in my case, but still unable to define the collection properly. I am compiled my code using g++ -std=c++17 main.cpp && ./a.out.
Code
#include <cstdlib>
#include <cmath>
#include <iostream>
#include <map>
#include <memory>
#include <typeinfo>
#include <queue>
#include <string>
#include <vector>
class Player
{
private:
int m_current_space = 1;
public:
Player() {}
void role_dice() {
m_current_space += floor( (rand()%10 + 1) / 3 );
}
int const get_current_space() {
return m_current_space;
}
void set_current_space(int current_space) {
m_current_space = current_space;
}
};
class Space
{
protected:
int m_id;
std::vector<Space> m_paths;
public:
Space() {} // requied to use [] operator in map
Space(int id) : m_id(id) {}
void add_path(Space& s) {
m_paths.push_back(s);
}
int get_id() {
return m_id;
}
virtual std::string class_type() = 0;
};
class Empty : public Space
{
public:
Empty(int id) : Space(id) {}
std::string class_type() {
return "Empty";
}
};
class Ladder : public Space
{
public:
Ladder(int id) : Space(id) {}
virtual void event(Player& p) {
p.set_current_space(1);
}
std::string class_type() {
return "Ladder";
}
};
class Snake : public Space
{
public:
Snake(int id) : Space(id) {}
virtual void event(Player& p) {
p.set_current_space(4);
}
std::string class_type() {
return "Snake";
}
};
class Board
{
private:
std::map<int, Space> m_board;
public:
void add_space(Space& s) {
m_board[s.get_id()] = s;
}
void draw_board() {
int i = 1;
for(auto const& [space_key, space] : m_board) {
if(i%3 == 0) {
std::cout << "○\n";
}
else if(typeid(space) == typeid(Snake)) {
std::cout << "○-";
}
else {
std::cout << "○ ";
}
++i;
}
}
void update_player_on_board(int position) {
int i = 1;
for(auto const& [space_key, space] : m_board) {
if(i%3 == 0) {
if (space_key == position) {
std::cout << "●\n";
}
else {
std::cout << "○\n";
}
}
else if(typeid(space) == typeid(Snake)) {
std::cout << "○-";
}
else {
if (space_key == position) {
std::cout << "● ";
}
else {
std::cout << "○ ";
}
}
++i;
}
}
const std::map<int, Space> get_board() {
return m_board;
}
friend std::ostream &operator<<(std::ostream& os, const Board& b) {
return os;
}
};
class GameStateManager
{
private:
std::string m_state = "game over";
bool m_playing = false;
public:
std::string const get_state() {
return m_state;
}
void set_state(std::string state) {
m_state = state;
}
};
int main()
{
std::cout << "Welcome to Bowser's 9 board game\n";
std::cout << "Start? y(yes) n(no)\n";
GameStateManager game_manager;
game_manager.set_state("playing");
auto space1 = std::make_shared<Space>(1);
auto space2 = std::make_shared<Space>(2);
auto space3 = std::make_shared<Space>(3);
auto space4 = std::make_shared<Space>(4);
auto space5 = std::make_shared<Space>(5);
auto space6 = std::make_shared<Space>(6);
auto space7 = std::make_shared<Space>(7);
auto space8 = std::make_shared<Space>(8);
auto space9 = std::make_shared<Space>(9);
std::vector<std::shared_ptr<Space>> v {
space1, space2, space3,
space4, space5, space6,
space7, space8, space9
};
Board bowsers_bigbad_laddersnake;
for(int i = 0; i < 10; ++i) {
bowsers_bigbad_laddersnake.add_space(*(v[i]));
}
bowsers_bigbad_laddersnake.draw_board();
Player mario;
int turn = 0;
while(game_manager.get_state() == "playing") {
std::cin.get();
std::cout << "-- Turn " << ++turn << " --" << '\n';
mario.role_dice();
bowsers_bigbad_laddersnake.update_player_on_board(mario.get_current_space());
if (mario.get_current_space() >= 9) {
game_manager.set_state("game over");
}
}
std::cout << "Thanks a so much for to playing!\nPress any key to continue . . .\n";
std::cin.get();
return 0;
}
You seem to have removed a lot of code to get into details here.
Have a Space pointer (smart or raw). Instantiate the specific space that you want, point to it with your pointer of type Space. Example std::shared_ptr<Space> pointerToSpace = std::make_shared<Snake> ("I'm a snake"); Now, without loss of generality, you can print the contents (of concrete type) with just the pointer to the space pointerToSpace->class_type(). Yes, you can have a collection of shared_ptrs in a container.
I am trying to create a class/struct that can take in a struct/class of different types that all have the function update().
I want to get the update() function and then put it in a vector as a pointer and then call it later, but I'm having trouble putting member function pointers into a vector, but I am having no problem putting 'classless' function pointers into the vector.
How can I put the member function pointers into the vector?
Here is my code
#include <vector>
#include <iostream>
#include <stdio.h>
using namespace std;
struct typeA
{
public:
int data = 0;
void update()
{
cout << "hello, my data is " << data << endl;
}
};
struct typeB
{
float data = 0;
void update()
{
cout << "hi, my data is " << data << endl;
}
};
class typeListTwo
{
typedef void(*updaterFunc)();
vector <updaterFunc> items;
public:
typeListTwo()
{
}
~typeListTwo()
{
items.~vector();
}
void addItem(updaterFunc newItem)
{
items.push_back(newItem); //This works
}
void doWork()
{
for (unsigned int funcIndex = 0; funcIndex < items.size(); funcIndex++)
{
items[funcIndex]();
}
}
};
class typeList
{
typedef void(*updaterFunc)();
vector <updaterFunc> items;
public:
typeList()
{
}
~typeList()
{
items.~vector();
}
template <class Item>
void addItem(Item newItem)
{
items.push_back(newItem.update); //But this does not?
//newItem.update(); //This also works by itself
}
void doWork()
{
for (unsigned int funcIndex = 0; funcIndex < items.size(); funcIndex++)
{
items[funcIndex]();
}
}
};
void aFunc()
{
cout << "123 hello" << endl;
}
void bFunc()
{
cout << "456 goodbye" << endl;
}
int main()
{
typeA aThing;
typeB bThing;
typeList listThings;
typeListTwo listThingsTwo;
aThing.data = 128;
bThing.data = -3.234;
listThings.addItem(aThing);
listThings.addItem(bThing);
listThings.doWork();
listThingsTwo.addItem(aFunc);
listThingsTwo.addItem(bFunc);
listThingsTwo.doWork();
return 0;
}
The way to go is to use std::function or use interface:
class typeList
{
std::vector<std::function<void()>> items;
public:
template <class Item>
void addItem(Item& item)
{
// Care: life time of item should be bigger than this instance
items.push_back([&](){ item.update(); });
}
void doWork()
{
for (auto& f : items)
{
f();
}
}
};
http://ideone.com/1ohrsO
The push_back called inside the constructor of static_constructor, is not reflected. Why?
#include <iostream>
#include <vector>
#include<memory>
#include<string>
using namespace std;
class has_static_constructor
{
public:
friend class static_constructor;
static vector<int> v;
class static_constructor
{
public:
vector<int> * upt; //&v;
static_constructor()
{
cout<<"inside static_constructor";
upt = &has_static_constructor::v;
has_static_constructor::v.push_back(1);
has_static_constructor::v.push_back(20);
}
} ;
static std::unique_ptr<has_static_constructor::static_constructor> upt ;
};
unique_ptr<has_static_constructor::static_constructor> has_static_constructor::upt(new has_static_constructor::static_constructor());
vector< int > has_static_constructor::v(2,100);
int main() {
// your code goes here
for (std::vector<int>::const_iterator i = has_static_constructor::v.begin(); i != has_static_constructor::v.end(); ++i)
{ std::cout << *i << ' ';
cout<<"\n I was here\n";
}
return 0;
}
Output:
inside static_constructor100
I was here
100
I was here
static_constructor() is called before has_static_constructor::v initialization.
Move
unique_ptr<has_static_constructor::static_constructor> has_static_constructor::upt(new has_static_constructor::static_constructor());
after
vector< int > has_static_constructor::v(2,100);
to have expected behaviour.
But better avoid those global entirely.
You might want to have a look at this way of ordering the code. It removes all initialisation-order dependencies, and in my view neatly separates the public interface from the internal implementation of the static data.
#include <iostream>
#include <vector>
class has_static_constructor
{
// note - all private
struct static_data {
static_data()
: _v(2, 100)
{
_v.push_back(1);
_v.push_back(20);
}
std::vector<int> _v;
};
static static_data& statics() {
static static_data sd;
return sd;
}
// public interface
public:
static std::vector<int>& v() { return statics()._v; }
};
auto main() -> int
{
for (const auto& i : has_static_constructor::v())
{
std::cout << i << std::endl;
}
return 0;
}
expected output:
100
100
1
20