How should I store templated functions in any container? - c++

I have a templated Prob class that acts as a way to organize various programming problems from different problem sets. The template is the problem number. How would I go about storing different Prob objects in a vector or map?
Here is the class declaration:
template<int ProbNum>
class Prob
{
std::string
text(std::ostream& out)
{
out << "Prob" << ((ProbNum < 10) ? "0" : "") << ProbNum << ": ";
}
void solve(std::ostream& out);
};
So in other words if I want to declare an object for problem 1 of some problem set I would do
Prob<1> p1;
and then store that in a map or vector for later use so the user can call on it during runtime (since you cannot pass a runtime argument into a template).
Edit: I want to use this class as an abstract class for other Prob classes.
Edit2: Added more code for clarification.
Edit3:
Top half is Prob1.hpp
Bottom half is a driver file on how I want to use it.
#include <iostream>
#include "Prob.hpp"
template<>
void
Prob<1>::solve(std::ostream& out)
{
out << "solution to prob1";
}
/***************************************************/
#include <iostream>
#include <cstdlib>
#include "Prob.hpp"
// Finished Problems
#include "Prob1.hpp"
int
main(int argc, char* argv[])
{
Prob<1> p;
p.solve(std::cout);
}

Each instance of a template class constitutes a different type. Hence, containers like std::vector cannot hold Prob<ProbNum> for different values of ProbNum.
If you know at compile time the number of Prob<ProbNum> instances you want, and the corresponding values of the template parameter int ProbNum you could store everything into a tuple. For example:
auto mycollection = std::make_tuple(Prob<1>(), Prob<2>());
A more general solution could be to define an abstract base class for Prob. Then you can manage to store a vector of Prob<ProbNum> objects, with inhomogeneous values of int ProbNum, if you define a vector of pointers to the base class. For this to work you must provide the interface in the base class, i.e., every member of Prob<ProbNum> that you want to access through the vector of the base class, must be virtual and already declared in the base class.
Consider the following example:
#include <iostream>
#include <memory>
#include <vector>
struct base {
virtual void print() const = 0;
virtual ~base() = default;
};
template <int i>
struct derived : public base
{
virtual void print() const { std::cout << i << std::endl; }
};
int main()
{
std::vector<std::unique_ptr<base>> vec;
vec.emplace_back(new derived<1>());
vec.emplace_back(new derived<3>());
vec.emplace_back(new derived<5>());
for (auto& el : vec)
el->print();
return 0;
}
The variable vec is essentially a vector of pointers to objects of type derived<i>, with inhomogeneous values of i. Because base::print() is virtual, it correctly resolves to the corresponding method of the derived<i> class.
Notice that I used a smart pointer to avoid memory leaking.
Also, it is important to declare virtual the destructor of base, see the discussion Why should I declare a virtual destructor for an abstract class in C++?.

Related

Member values not accessed in vector of instances of different subclasses

Jumping off of the above question Making a vector of instances of different subclasses : when implementing a vector of (pointers to) different subclasses (initialized as vector<Base*> objects), I expect to be able to access the correct member variables based on the subclass called.
Below is sample code:
#include <iostream>
#include <vector>
#include <memory>
using namespace std;
class Entity {
public:
int index;
Entity() {};
virtual ~Entity() {};
virtual void hit() = 0;
};
class Mesh : public Entity {
public:
int index;
Mesh(int x) {this->index=x;};
virtual void hit() {}
};
int main() {
vector<unique_ptr<Entity>> objects;
objects.push_back(unique_ptr<Entity>(new Mesh(35)));
objects.push_back(unique_ptr<Entity>(new Mesh(10)));
for ( int i = 0 ; i < objects.size() ; i++ )
cout << objects[i]->index << endl;
return 0;
}
Where I expect
35
10
to be printed, meanwhile I get instead.
0
0
How can I access the correct member variable values in this scenario?
The problem is due to a misunderstanding on inheritance. If you redefine your Mesh as follows, it would work as expected:
class Mesh : public Entity {
public:
//int index; // No don't redefine: here you'd have two different indexes
Mesh(int x) {this->index=x;};
void hit() override {} // extraa safety: use override instead of virtual to be sure to override
};
Online demo
Not related to the problem, but some more thoughts:
You can of course only use the interface of the base class (here Entity).
It is not very prudent to expose the index member publicly. A safer option would be to have it private, and access it via a public getter. If the index should be set only at construction, the Entity constructor should take care of it, otherwise you could consider a public setter.

Multi-inheritance with interfaces type casting on pointers for using with std::list

I come from Java (OOP) background. I made a simple class to illustrate my problem:
#include <list>
#include <string>
#include <iostream>
// classes
class InterfaceA
{
public:
virtual std::string functionA();
};
class InterfaceB
{
public:
virtual std::string functionB();
};
class DerivedAB : public InterfaceA, public InterfaceB
{
public:
std::string functionA()
{
return "I'm a A object";
}
std::string functionB()
{
return "I'm a B object";
}
};
// functions
void doStuffOnListOfA(std::list<InterfaceA*> aElements)
{
std::cout << "Print list of A" << std::endl;
for (InterfaceA* const& a : aElements)
{
std::cout << a->functionA() << std::endl;
}
};
int main()
{
std::list<DerivedAB*> derivedABs;
doStuffOnListOfA(derivedABs);
return 0;
}
I have two simple virtual classes InterfaceA and InterfaceB and a class DerivedAB that multi-inherits the two first virtual classes.
Furthermore, I then create a list of pointers of DerivedAB (std::list<DerivedAB *>) and wish to use this list with a function designed to work on a list of InterfaceA-derived objects. But I get an error:
(base) ❮ onyr ★ kenzae❯ ❮ multi_inheritance_type_convertion❯❯ make
g++ -c -o main.o main.cpp
main.cpp: In function ‘int main()’:
main.cpp:54:32: error: could not convert ‘derivedABs’ from ‘std::__cxx11::list<DerivedAB*>’ to ‘std::__cxx11::list<InterfaceA*>’
doStuffOnListOfA(derivedABs);
I have obviously a type casting error. I have read many articles on Stack Overflow about the different casting in C++ as well as on Multi-Inheritance, but my brain refuses to give me the answer.
Edits:
I said an erroneous statement:
"However I'm pretty sure such a code would work in Java..."
Apparently I'm missing an important concept about type inheritance...
C++ is far different from Java!
I have obviously a type casting error.
You are right about this (aka. type mismatch)! The std::list is a standard template container which gives you a concrete type, when you instantiate with a template argument.
That means, the std::list<InterfaceA*> is different from std::list<DerivedAB *>.
This is exactly the compiler tells you:
error: could not convert
from ‘std::__cxx11::list<DerivedAB*>’ ----> i.e. std::list<DerivedAB*>
to ‘std::__cxx11::list<InterfaceA*>’ ----> i.e std::list<InterfaceA*>
doStuffOnListOfA(derivedABs); ----> at the function call
You can not implicitly(i.e. compiler will not) convert to one another.
You need to cast each element of the derivedABs to base pointers or (in your case) make the doStuffOnListOfA as template function:
template<typename T>
void doStuffOnListOfA(std::list<T*> aElements)
{
std::cout << "Print list of A" << std::endl;
for (InterfaceA* a : aElements)
{
std::cout << a->functionA() << std::endl;
}
};
To make sure that, one use the above only for std::list<derived from InterfaceA and B>, you may can (optionally) SFINAE the template function:
#include <type_traits> // std::is_base_of
template<typename T>
constexpr bool isBaseOfInterfaces = std::is_base_of_v<InterfaceA, T> && std::is_base_of_v<InterfaceB, T>;
template<typename T>
auto doStuffOnListOfA(std::list<T*> aElements)
-> std::enable_if_t<isBaseOfInterfaces<T>, void>
{
// ... code
};
That being said,
You need to look into the smart pointers (such as std::unique_ptr, std::shared_ptr) rather than using raw pointers (manual memory management), by which you can handle the memory management smartly.
You might want to add the virtual destructor in your base classes for a defined behavior. See here for more: When to use virtual destructors?
Here is (the complete demo)

C++ pass generic vector as function parameter

I am fairly new to C++ and templates. I dont expect the reason why it doesnt work to be very complex, but I'm just not getting it.
void print(vector<> v) {
return;
}
does not compile with the error
error: wrong number of template arguments (0, should be at least 1)
however
void print(vector<int> v) {
return;
}
doesn't yield such an error.
Why is that?
You must make your function template to accept more than one type:
template <typename T>
void print(vector<T> v) {
return;
}
If you are coming from another language (I think Java uses the syntax you proposed?), I suggest getting a good C++ book to learn from. It's going to be much less painful to learn properly from start than trying to apply your knowledge from other languages in C++.
If you need to store elements of different types, you should look into polymorphism and class hierarchies. For example, if you had two different classes A and B, and needed a vector to hold either of those, you could make sure that they share a common base class.
You could then store pointers or references to such objects in one and the same vector, like so (using smart pointers in this case):
#include <iostream>
#include <memory>
#include <vector>
class Base {
public:
virtual ~Base() {}
virtual void print() = 0;
};
using BasePtr = std::shared_ptr<Base>;
class A : public Base {
public:
virtual void print() override { std::cout << "I'm an A" << std::endl; }
};
class B : public Base {
public:
virtual void print() override { std::cout << "I'm a B" << std::endl; }
};
void print(const std::vector<BasePtr>& v) {
for (auto&& i : v)
i->print();
}
int main()
{
std::vector<BasePtr> v;
v.push_back(std::make_shared<A>()); /* create and add an element of type A */
v.push_back(std::make_shared<B>()); /* create and add an element of type B */
print(v);
}
(Also, note that I'm passing the vector type as const reference, otherwise it would be copied before being passed into the function.)

How can I have several inherited classes together in the same array?

I have a few classes, ObjDef, PeopDef, NpcDef, and PlyDef, such that PlyDef and NpcDef each seperately inherit PeopDef, and PeopDef inherits ObjDef. Each class has functionality that builds on the class before it, so it's important that PeopDef::Tick is called before ObjDef::Tick. I have every object stored in a vector<ObjDef> object, but when the main tick loop goes through them, I want them to call the original classes' Tick, rather than ObjDef::Tick, which is what the vector<ObjDef> currently makes it do. Is there any way to do this, or do I have to have a separate vector for each class?
You can store an ObjDef pointer (ObjDef* or a smart pointer) in the vector and make the Tick method virtual.
Here's an example:
#include <iostream>
#include <vector>
#include <memory>
class ObjDef
{
public:
virtual void Tick()
{
std::cout << "ObjDef::Tick\n";
}
};
class PeopDef : public ObjDef
{
public:
virtual void Tick()
{
std::cout << "PeopDef::Tick\n";
}
};
int main()
{
std::vector<std::shared_ptr<ObjDef>> objects;
std::shared_ptr<ObjDef> obj(new ObjDef());
std::shared_ptr<ObjDef> peop(new PeopDef());
objects.push_back(obj);
objects.push_back(peop);
for (auto object : objects)
{
object->Tick();
}
return 0;
}

C++ : Vector of template class

I have a template class named Cell as follows:-
template<class T>class Cell
{
string header, T data;
}
Now I want another class Named Row. Row will have a vector named Cells such that I can add both Cell and Cell type elements to that vector. Is it possible?
If so, how can I do that?
Thanks in advance.
With the extra detail you've provided, the first two answers won't work. What you require is a type known as a variant for the cell and then you can have a vector of those. For example:-
enum CellType
{
Int,
Float,
// etc
};
class Cell
{
CellType type;
union
{
int i;
float f;
// etc
};
};
class Vector
{
vector <Cell> cells;
};
This, however, is a pain to add new types to as it requires a lot of code to maintain. An alternative could use the cell template with a common base class:-
class ICell
{
// list of cell methods
};
template <class T>
class Cell : public ICell
{
T data;
// implementation of cell methods
};
class Vector
{
vector <ICell *> cells;
};
This might work better as you have less code initially to update to add a new cell type but you have to use a pointer type in the cells vector. If you stored the cell by value, vector <ICell>, then you will lose data due to object slicing.
The reason why this is NOT possible in C++, but possible in Java/Python is because: in a C++ vector, the STL container's storage (returned by vector::data()) contains all the object instantiations sequentially packed. In which each element must have the same size. This makes addressing fast and convenient. Therefore, suppose you define a template class A,
template <class T>
class A{
int id;
T obj;
};
Its size will depend on the template variable "T obj". Pushing the same class A of a different template type T will make each element in the vector have different sizes, thus, this is impossible. The only way is to use the vector of shared_ptr or unique_ptr of a base class. Both shared_ptr and unique_ptr are supported by C++11 and Boost. Each derived-class element can have different template types. In this way, when the base class pointer's destructor is called, the derived class's destructor will be invoked. For example,
#include <memory>
#include <vector>
#include <iostream>
#include <string>
using namespace std;
class A{};
template <class T>
class AImpl : public A{
public:
T obj;
AImpl(T _obj):obj(_obj){}
~AImpl(){
cout << "Deleting " << obj << endl;
}
};
int main(int argc, char** argv)
{
AImpl <string>* a1 = new AImpl <string> ("string1234");
AImpl <int>* a2 = new AImpl <int> (1234);
AImpl <double>* a3 = new AImpl <double> (1.234);
vector <shared_ptr<A>> As;
As.push_back(shared_ptr<A>(a1));
As.push_back(shared_ptr<A>(a2));
As.push_back(shared_ptr<A>(a3));
}
Remember to compile with -std=c++11 to enable C++11.
Output:
Deleting string1234
Deleting 1234
Deleting 1.234
And you get what you want! :)
In Java/Python, every class-object variable is actually a pointer, thus, a Java Array of A or a Python list of A is equivalent to a C++ array of pointers of A. Thus, you get essentially the same functionality without explicitly creating shared_ptrs.
The other answer is good, but you probably wanted:
template<class T>
class Row
{
private:
class Cell {
string header;
T data;
}
std::vector<Cell> cells;
...
}
Something like this?
template<class T>
class Row
{
private:
std::vector<Cell<T> > cells;
};
Okay, this answer is incorrect.
So, if you want to store in one vector different cells - you should use some dynamic type identification (you can use one base-class and store pointer to it in vector, that use only virtual functions, that are overrided in all derived classes, you can store something like boost::any and save some type-identification for each inserted element, for cast them into real type and work with it).