If I have a base class A I would like to be able to write code in Athat uses an array whose size is determined by its children
I want to be able to have an array of pointers to A, but I don't want to have a separate member function created for each length
The best I could come up with is as follows:
class A
{
char type;
int * values;
int calc(int pos); // implementation is not relevant to question
public:
A (int * arr) : values(arr) {}
int foo(int pos)
{
int index=calc(pos);
return values[index];
}
};
template <size_t X>
class B : public A
{
int vals[X];
public:
B() : A(vals) {}
};
template<>
class B<0>; // don't want 0 allowed
This allows A to access an array where the child determines the size, and it is contiguous memory
But, it wastes the space of the values pointer and obfuscates information the compiler could use for optimization since as implemented it doesn't have to be contiguous memory that the child passes on construction, but I would like to require continuous memory.
Ideally I would like to use the offset directly in A
In C this works as int values[] but there is no C++ equivalent
Yes you can, using templates:
template<std::size_t size>
struct A {
A(std::array<int, size> _values) : values{_values} {}
private:
std::array<int, size> values;
};
struct B : A<4> {
using A<4>::A;
};
Then, you can use your class like this:
B myB{5, 6, 3, 2};
std::array allocates memory on the stack, or in the struct directly, just like a fixed array. You can test this by comparing sizes.
If you need a common base class, you can do this:
struct C {
virtual ~C() {}
virtual int* data();
virtual std::size_t size();
};
And then overriting theses in A:
template<std::size_t size>
struct A : C {
A(std::array<int, size> _values) : values{_values} {}
int* data() override {
return values.data();
}
std::size_t size() override {
return size;
}
private:
std::array<int, size> values;
};
The classic way to implement this is using inheritance and virtual members:
class A {
virtual int& value_at(size_t pos);
// other interesting methods
};
// subclass of A that uses std::vector for storage
class B: public A {
std::vector<int> storage;
int& value_at(size_t pos) {
return storage[pos];
}
};
// subclass of A that uses a fixed-size array for storage
template<int N>
class C: public A {
int storage[N];
int& value_at(size_t pos) {
return storage[pos];
}
};
B b; // ...initialize b...
C<10> c; // ...initialize c...
A *a1 = &b;
A *a2 = &c;
// call a1->value_at(), a2->value_at() to access arrays
// transparently, regardless of storage (along with other
// public methods of A).
This approach will require A::value_at to dispatch via a virtual table or equivalent mechanism. If it is known at compile-time which storage strategy will be used, you can make A a template class:
template<typename T>
class A: public T {
// other interesting methods, that use value_at() from T
};
class vec_storage {
std::vector<int> storage;
public:
int& value_at(size_t pos) {
return storage[pos];
}
};
// subclass of A that uses a fixed-size array for storage
template<int N>
class array_storage {
int storage[N];
public:
int& value_at(size_t pos) {
return storage[pos];
}
};
A<vec_storage> b;
A<array_storage<10>> c;
In this example, b and c will perform without the additional indirection at run-time, but at the cost that there is no generic base class, so a function expecting some A & cannot be passed a reference to either b or c.
For just the basic ability to change the size of the object's array, you're probably looking for templates, as Fred Larson pointed out. It's actually quite simple...
template <const int arraySize>
class MyClass
{
private:
int vals[arraySize];
}
At this point, you don't even need any derived classes. You're good to go, and can work with any size on the fly.
Bear in mind, however, that MyClass is not a valid type in this scenario. Because this is a template, you must specify the size.
MyClass wrong; //This will throw an error. MyClass is not a type.
MyClass<64> right; //This is a proper use of the type.
If you need the additional ability to store those objects of different sizes, you can combine this with inheritance fairly easily using a virtual [but not abstract] base class.
(Please ignore the crufty design, as I haven't bothered to define my constructors/destructors in this example.)
class BaseClass
{
public:
BaseClass(){}
virtual int access(int i)
{
return 0;
}
virtual ~BaseClass(){}
};
template <const int arraySize>
class MyClass : public BaseClass
{
public:
MyClass(){}
int access(int i)
{
return vals[i];
}
~MyClass(){}
private:
int vals[arraySize];
};
WARNING: It is critical that the base NOT be abstract if you want to be able to store your derived classes in a vector. See this question.
In this example, of course, you may want to create wrapper functions for accessing the array.
Related
I use a base class (A) that manages some data but without having the storage. The derived class (B) has a storage member and initializes the base class (A) with a pointer to that storage and the size of them.
The code model (clang) in the IDE gives me a warning "Field mStorage is uninitialized when used here" at line
explicit B() : A(mStorage.data(), 10) {}
Question 1: Is this a problem as long as I do not use the storage in the base class constructor?
Question 2: If this doesn't cause a problem, is there a way to avoid this warning?
class A
{
public:
explicit A(int* p, size_t s)
: mPtr(p), mSize(s)
{}
void append(int i) { /* ... */ }
private:
int* mPtr = nullptr;
size_t mSize = 0;
};
template <size_t N>
class B : public A
{
public:
explicit B() : A(mStorage.data(), N) {}
private:
std::array<int, N> mStorage {};
};
Update:
add template <size_t N> to class B
My intension is to decouple the normal usage of the class and the template size in class B
void worker_function(const A& a)
{
a.append(int(1));
}
// and also
struct Foo
{
Foo(const A& a) : m_a(a) {}
void do_some_work()
{
a.append(int(1));
}
const A& m_a;
};
void main()
{
B<10> b;
worker_function(b);
// and also
Foo foo(b);
foo.do_some_work();
}
This might work as you intend, but compiler warnings should not be ignored. mStorage doesn't exist yet at the time A is constructed. The base class is constructed first. Maybe the compiler, looking at mStorage, will peek ahead, so to speak, but that's not the required sequence. mStorage is probably just random garbage.
From the skeleton, it's hard to guess what the intent is, but you could easily solve the problem by making a virtual function that returns a pointer to the storage in the derived class. You may also find some sort of template solution.
I suggest you tell us a little more about why you want to design a class this way.
UPDATE:
C++ is unhappy not knowing what N is for a good reason. What happens when worker_function is called for the (N+1)st time? đŸ’¥
I can think of two safer approaches. One is to make append a (pure?) virtual function. It won't be easy to write A::append anyway, not to mention avoiding a range error. The other is simply to use std::vector instead of std::array in class A and not try this fancy derivation scheme.
You could use composition instead of inheritance and put the classes in the right order.
Note that I had to do some fiddeling with your constness to get it to actually work.
Example online here.
#include <array>
#include <cstddef>
class A
{
public:
explicit A(int* p, size_t s)
: mPtr(p), mSize(s)
{}
void append(int i) { mPtr[i] = i; ++i; } // My guess
private:
int* mPtr = nullptr;
size_t mSize = 0;
};
template <size_t N>
class B
{
public:
explicit B() : m_a(mStorage.data(), N) {}
operator A&() {
return m_a;
}
private:
std::array<int, N> mStorage {};
A m_a;
};
void worker_function(A& a)
{
a.append(int(1));
}
// and also
struct Foo
{
Foo(A& a) : m_a(a) {}
void do_some_work()
{
m_a.append(int(1));
}
A& m_a;
};
int main()
{
B<10> b;
worker_function(b);
// and also
Foo foo(b);
foo.do_some_work();
}
unfortunately, I can't use std::vector and have to use plain C++ arrays. I got the following code:
class Base
{
}
class DerivedCar : Base
{
public:
DerivedCar(int a) a(a) {};
private:
int a;
}
class DerivedHouse : Base
{
public:
DerivedHouse(float b) b(b) {};
private:
float b;
}
class Vector
{
Vector() :
index(0)
void add(const DerivedCar& car)
{
vec[index] = new DerivedCar(car.a);
index++;
}
void add(const DerivedHouse& house)
{
vec[index] = new DerivedHouse(house.b);
index++;
}
private:
Vector vec[100];
int index;
}
int main()
{
Vector vector;
DerivedCar car(100);
DerivedHouse house(2.f);
vector.add(car);
vector.add(house);
}
I would like to have an array of type Base and add objects of a derived type.
Is there a better approach to this other than the way I did? What would be the best way to keep copying of objects at a minimum.
How to add derived class objects to an array of base class type?
You can not put derived class objects into raw array or std::vector of base class because the derived class objects are usually larger and so simply do not fit there.
Is there a better approach to this other than the way I did?
Better approaches are certainly out there. One good example of such containers with polymorphic elements is boost::base_collection. Read its documentation and its source code. If you do not understand some detail in it then ask about that detail in Stack Overflow.
What would be the best way to keep copying of objects at a minimum.
Containers that only contain pointers to objects and intrusive containers
keep copying of objects at minimum. However such containers do not
manage the objects and so responsibility of objects life time has
to be taken by something outside.
Here is a possible way that implements a linked list:
class Base
{
};
class DerivedCar : public Base
{
public:
DerivedCar(int a) { _a = a; };
private:
int _a;
};
class DerivedHouse : public Base
{
public:
DerivedHouse(float b) { _b = b; };
private:
float _b;
};
class Object
{
public:
const Base *data;
const Object *next;
};
class Vector
{
public:
void add(const Base& v)
{
Object item;
item.data = &v;
head.next = &item;
index++;
}
private:
Object head;
int index = 0;
};
int main()
{
Vector vector;
DerivedCar car(100);
DerivedHouse house(2.f);
vector.add(car);
vector.add(house);
}
I am working with a set of classes A, B, ... These classes are independent except that they have one method in common. Now I want to combine these classes in a vector, to call method in one loop. It seems that the best solution is to make the classes derived classes from some Parent (see below).
Now the question is the following. I want to create a header-only library for each class (a.h, b.h, ...). There I want the classes to be completely independent. Only in the main module I want to 'attach' the classes to a Parent to be able to combine them in a vector. How do I do this? Or do I have to resort to a vector of void* pointers? Or is there another way to combine these classes in a vector?
Classes in list: with parent/child paradigm
Here is what I have been able to do to combine the classes in the vector. Note I specifically want to avoid the parent/child paradigm in the class definitions. But I still want to combine them in a vector.
#include <iostream>
#include <vector>
#include <memory>
class Parent
{
public:
virtual ~Parent(){};
virtual void method(){};
};
class A : public Parent
{
public:
A(){};
~A(){};
void method(){};
};
class B : public Parent
{
public:
B(){};
~B(){};
void method(){};
};
int main()
{
std::vector<std::unique_ptr<Parent>> vec;
vec.push_back(std::unique_ptr<Parent>(new A));
vec.push_back(std::unique_ptr<Parent>(new A));
vec.push_back(std::unique_ptr<Parent>(new B));
for ( auto &i: vec )
i->method();
return 0;
}
Compile using e.g.
clang++ -std=c++14 main.cpp
A possible solution based on type erasure, static member functions and pointers to void that doesn't make use of virtual at all (example code, far from being production-ready):
#include <iostream>
#include <vector>
struct Erased
{
using fn_type = void(*)(void *);
template<typename T>
static void proto(void *ptr) {
static_cast<T*>(ptr)->method();
}
fn_type method;
void *ptr;
};
struct A
{
void method(){ std::cout << "A" << std::endl; };
};
struct B
{
void method(){ std::cout << "B" << std::endl; };
};
int main()
{
std::vector<Erased> vec;
vec.push_back(Erased{ &Erased::proto<A>, new A });
vec.push_back(Erased{ &Erased::proto<B>, new B });
for ( auto &erased: vec ) {
erased.method(erased.ptr);
}
return 0;
}
This can help to avoid using a common base class. See it on wandbox.
As mentioned in the comments, here is a slightly modified version that adds create and invoke methods to reduce the boilerplate for the users.
This is more of a pseudocode, trivial details are omitted.
struct HolderBase
{
virtual void foo() = 0;
};
template <class T>
struct Holder : HolderBase
{
Holder(T* t) : t(t) {}
T* t;
void foo() { t->foo(); }
};
std::vector<HolderBase*> v { new Holder<A>(new A), new Holder<B>(new B) };
You can also have a variant of Holder that holds an object by value (and mix both variants in the same vector freely).
If you have a single method to call, there is a much simpler solution:
A a;
B b;
std::vector<std::function<void()> v { [](){a.foo();}, [](){b.foo();} };
You want to erase the type of the objects and treat them uniformly, so naturally type erasure is the solution.
class with_method_t {
struct model_t {
virtual ~model_t() = default;
virtual void call_method() = 0;
};
template<class C>
class concept_t final : public model_t {
C obj;
public:
concept_t(C const& c) : obj{c} {}
concept_t(C&& c) : obj{std::move(c)} {}
void call_method() override { obj.method(); }
};
std::unique_ptr<model_t> instance;
public:
template<class C>
with_method_t(C&& arg)
: instance{std::make_unique<concept_t<C>>(std::forward<C>(arg))}
{}
void method() { instance->call_method(); }
};
Then have yourself a vector of with_method_t which is a value type. No raw dynamic allocation or de-allocation. The instance is build by forwarding the argument it receives into a small polymorphic container:
std::vector<with_method_t> vec;
vec.emplace_back(A{});
vec.emplace_back(B{});
for ( auto &i: vec )
i.method();
I have a base class like:
class Base
{
public:
virtual void fun() const =0;
};
class Derived: public Base
{
virtual void fun()
{
//implemtation of fun
}
};
I have a global structure:
struct Mystruct {
int a;
char *b;
} MYSTRUCT;
Then i added the structure to a vector:
List = new MYSTRUCT;
vector<MYSTRUCT*> SS;
SS.push_back(List);
How can i pass the this vector to the fun function and access the struct in the function?
You seem confused about the meaning of this:
struct Mystruct
{
int a;
char *b;
}MYSTRUCT;
This is a declaration of a struct called Mystruct and an instance of Mystruct called MYSTRUCT. So when you create std::vectors or std::lists to hold these structs, you need to use the type as template parameter:
std::vector<Mystruct> v0; // vector holding 0 Mystructs
If you want a vector holding pointers, you need
std::vector<Mystruct*> v1;
This will simply not compile because MYSTRUCT is not a type:
std::vector<MYSTRUCT*>
Just like that:
class Base
{
public:
virtual void fun(const std::vector<Mystruct*>& list) const =0;
};
class Derived:public Base
{
public:
virtual void fun(const std::vector<Mystruct*>& list)
{
//implemtation of fun
}
};
But your sample shows that you might have other problems understanding how to design C++ classes (e.g. you don't need to use this C like struct definition syntax with C++).
I want to have a container (let's say an std::vector) that would hold various inherited types, and would instantiate them,.i.e. vector of classes --> vector of objects.
For instance:
class A{};
class B: public class A
{};
class C: public class A
{};
void main()
{
std::vector<of inherited A types> typesVec;
std::vector<A*> objectsVec;
typesVec.push_back(class B);
typesVec.push_back(class C);
for (int i = 0; i < typesVec.size(); i++)
{
A* pA = new typesVec.at(i);
objectsVec.push_back(pA);
}
}
Thanks in advance..
This isn't possible in C++ (at least not directly). I can see this happening in a language that has reflection, but C++ doesn't.
What you can do instead is create a factory or simply methods that create objects of the specified type.
Instead of having a vector of types, you'd have a vector of object generators (close enough, right?):
class A{};
class B: public class A
{};
class C: public class A
{};
struct AFactory
{
virtual A* create() { return new A; }
};
struct BFactory : AFactory
{
virtual A* create() { return new B; }
};
struct CFactory : AFactory
{
virtual A* create() { return new C; }
};
//...
typesVec.push_back(new BFactory);
typesVec.push_back(new CFactory);
for (int i = 0; i < typesVec.size(); i++)
{
A* pA = typesVec.at(i)->create();
objectsVec.push_back(pA);
}
There is a reusable approach with templates. This is a generic factory for derived types that comes with an install and a create method which lets you write code like this:
int main() {
TypeVector<Base> t;
t.install<Foo>("Foo");
t.install<Bar>("Bar");
t.create("Foo")->hello();
}
Note it's a sketch implementation. In the real world, you may provide another template parameter to specify the underlying container type (for few types, vector is probably more efficient than set).
The type-vector is this:
template <typename Base>
class Creator;
template <typename Base>
class TypeVector {
public:
template <typename Derived>
void install (std::string const &name) ;
std::shared_ptr<Base> create (std::string const &name) const;
private:
struct Meta {
Meta(std::shared_ptr<Creator<Base>> creator, std::string const &name)
: creator(creator), name(name) {}
std::shared_ptr<Creator<Base>> creator;
std::string name;
};
std::vector<Meta> creators_;
};
We somehow need a way to store the type in an allocatable manner. We do it like boost::shared_ptr, which combines an abstract base class and a template derived class:
template <typename Base>
class Creator {
public:
virtual ~Creator() {}
virtual std::shared_ptr<Base> create() const = 0;
};
template <typename Base, typename Derived>
class ConcreteCreator : public Creator<Base> {
public:
virtual std::shared_ptr<Base> create() const {
return std::shared_ptr<Base>{new Derived()};
}
};
The "concrete creator" is able to allocate an actual object, and return a pointer-to-base of it.
Finally, here are the implementations of TypeVector::install and TypeVector::create:
template <typename Base>
template <typename Derived>
void
TypeVector<Base>::install (std::string const &name)
{
creators_.emplace_back(
std::shared_ptr<Creator<Base>>(new ConcreteCreator<Base, Derived>()),
name);
}
template <typename Base>
std::shared_ptr<Base>
TypeVector<Base>::create (std::string const &name) const
{
for (auto m : creators_) {
if (name == m.name) return m.creator->create();
}
throw std::runtime_error("...");
}
and finally, here's a test:
#include <iostream>
struct Base {
virtual ~Base() {}
virtual void hello() const = 0;
};
struct Foo : Base {
virtual void hello() const { std::cout << "I am a Foo\n"; }
};
struct Bar : Base {
virtual void hello() const { std::cout << "I am a Bar\n"; }
};
int main() {
TypeVector<Base> t;
t.install<Foo>("Foo");
t.install<Bar>("Bar");
t.create("Foo")->hello();
}
You can go further and make any constructor callable for code like ...
...
Bar(Color, Age, int)
...
t.create("Foo", Color::Red, Age::TooOld, 42)
... but this requires an awesome grasp of variadic template argument lists, and how to fold them into a constructor call (can be done and has been done, but it would explode this answer).
Just a quick solution sketch:
The C++ standard does not provide direct calls to constructors. As such you can't have function pointers to constructors; you can, however, have a wrapper function "create", something like:
template<typename T>
T* create () {
return (new T();
}
Provide overloaded create definitions for one argument, two arguments, ... or try to use variadic templates; or, if you already know what types you need, you can create the create functions specifically. Then you can have a function pointer to the create function:
&create<TheType>
Mind that the signature of this function however depends on the type used. You can however create a struct that contains typdefs for the templated type, a typedef for the type pointer, and the create function as a functor operator().
Thus you can have two vectors, one for the function pointers to the create function, or, alternatively to the structs mentioned before, and one with the actual objects. In your case where you only have inherited types, you might be able to define functions A* createB() { return new B(); }, A* createC() { return new C(); }, ... for each inherited type B, C, ... and have a vector for pointers to these create functions and the second vector for the A pointers.
I might point you Andrei Alesandrescu´s book Modern C++ Design (or the Loki library he describes in the book) and the chapter about type lists. This would require you to do the typeVec.insert( type ) at compile time.