Short version of question
I seek advice as to whether to use ./*this versus ->/this,
i.e. C++ (*this).chained().methods() versus this->chained()->methods().
By the way, at the moment most of the pages I have seen recommend
[[C++ (*this).chained().methods()]].
I was just wondering, because you can't do
My_Class object.chained().methods();
(By the way, I have not tested the examples in this first section. I provide tested examples in the second section.)
You must do
My_Class object;
object.chained().methods();
which is an annoying extra line
Or you can do
My_Class object = My_Class().object.chained().methods();
which requires a value copy - not acceptable if the constructor has side effects, like registering the object instance - like so many Knobs libraries do
Or you can do
My_Class* object_ptr = *(new My_Class).object.chained().methods();
which works, but requires that annoying *(ptr)
Or you can do
My_Class* object_ptr = (new My_Class)->object.chained()->methods();
which is a teensy bit better.
I suppose you can do
My_Class& object_ref(My_Class().chained().methods());
and I am not sure what I think about that.
By the way, I do NOT need debugging help here.
I code stuff like this up all the time
I provide the examples only for clarity.
I am seeking style advice, because there are several ways to code it,
and I have used different libraries that do it in opposite ways.
And mixing them is ugly:
My_Object_with_Setters* object_ptr2 = &((new My_Object_with_Setters)->set_R1(1).set_P1(2)->set_R1(3))
My_Object().method_returning_ptr()->method_returning_ref();
Maybe it's not that bad.... but it sure can be confusing.
When I run into code that uses two different libraries using mixed .chained()->methods()
I sometimes wish for the ability to have postfix address-of and dereference operators
My_Object* mptr = My_Object() .method_returning_ptr() -> method_returning_ref ->&
More complete Examples
Setter Functions
I most often use this idiom with setter functions
class My_Object_with_Setters {
public:
static int count;
int value;
public:
My_Object_with_Setters() {
++count;
value = 0;
}
public:
std::ostream& print_to_stream(std::ostream& ostr) const {
ostr << "(" << this->count << "," << this->value << ")";
return ostr;
}
friend std::ostream&
operator<< (
std::ostream& ostr,
const My_Object_with_Setters& obj ) {
return obj.print_to_stream(ostr);
}
public:
My_Object_with_Setters& set_R1(int val) {
this->value = val;
std::cout << "set_R1: " << *this << "\n";
return *this;
}
My_Object_with_Setters& set_R2(int val) {
this->value = val;
std::cout << "set_R2: " << *this << "\n";
return *this;
}
public:
My_Object_with_Setters* set_P1(int val) {
this->value = val;
std::cout << "set_P1: " << *this << "\n";
return this;
}
My_Object_with_Setters* set_P2(int val) {
this->value = val;
std::cout << "set_P2: " << *this << "\n";
return this;
}
public:
My_Object_with_Setters set_V1(int val) {
this->value = val;
std::cout << "set_V1: " << *this << "\n";
My_Object_with_Setters retval;
retval = *this; // kluge to force new object
return retval;
}
My_Object_with_Setters set_V2(int val) {
this->value = val;
std::cout << "set_V2: " << *this << "\n";
My_Object_with_Setters retval;
retval = *this; // kluge to force new object
return retval;
}
};
int My_Object_with_Setters::count = 0; // clas static, distinguishes instances
void test_My_Object_with_Setters()
{
std::cout << "cascading ref, ref, copy, copy, ref, ref\n";
My_Object_with_Setters object;
object.set_R1(1).set_R2(2).set_V1(11).set_V2(12).set_R1(101).set_R2(102);
std::cout << "cascading ptr, ptr, ptr, ptr\n";
My_Object_with_Setters* object_ptr = (new My_Object_with_Setters)->set_P1(1)->set_P2(2)->set_P1(11)->set_P2(12);
std::cout << "cascading &address-of, ptr, ptr\n";
(&object)->set_P1(1)->set_P2(2);
std::cout << "cascading new ptr ref ptr ref\n";
My_Object_with_Setters* object_ptr2 = &(*(new My_Object_with_Setters)->set_R1(1).set_P1(2)).set_R1(3);
}
Test output:
cascading ref, ref, copy, copy, ref, ref
set_R1: (1,1)
set_R2: (1,2)
set_V1: (1,11)
set_V2: (2,12)
set_R1: (3,101)
set_R2: (3,102)
cascading ptr, ptr, ptr, ptr
set_P1: (4,1)
set_P2: (4,2)
set_P1: (4,11)
set_P2: (4,12)
cascading &address-of, ptr, ptr
set_P1: (4,1)
set_P2: (4,2)
cascading new ptr ref ptr ref
set_R1: (5,1)
set_P1: (5,2)
set_R1: (5,3)
Generic Example
class My_Object {
public:
static int count;
public:
My_Object() {
++count;
}
public:
My_Object& method1_returning_ref_to_current_object() {
std::cout << count << ": method1_returning_ref_to_current_object\n";
return *this;
}
My_Object& method2_returning_ref_to_current_object() {
std::cout << count << ": method2_returning_ref_to_current_object\n";
return *this;
}
public:
My_Object* method1_returning_ptr_to_current_object() {
std::cout << count << ": method1_returning_ptr_to_current_object\n";
return this;
}
My_Object* method2_returning_ptr_to_current_object() {
std::cout << count << ": method2_returning_ptr_to_current_object\n";
return this;
}
public:
My_Object method1_returning_value_copy_of_current_object() {
std::cout << count << ": method1_returning_value_copy_of_current_object\n";
My_Object retval;
return retval;
}
My_Object method2_returning_value_copy_of_current_object() {
std::cout << count << ": method2_returning_value_copy_of_current_object\n";
My_Object retval;
return *this;
}
};
int My_Object::count = 0; // clas static, distinguishes instances
void test_My_Object()
{
std::cout << "cascading ref, ref, copy, copy, ref, ref\n";
My_Object object;
object
.method1_returning_ref_to_current_object()
.method2_returning_ref_to_current_object()
.method1_returning_value_copy_of_current_object()
.method2_returning_value_copy_of_current_object()
.method1_returning_ref_to_current_object()
.method2_returning_ref_to_current_object()
;
std::cout << "cascading ptr, ptr, ptr, ptr\n";
My_Object* object_ptr = new My_Object;
object_ptr
->method1_returning_ptr_to_current_object()
->method2_returning_ptr_to_current_object()
->method1_returning_ptr_to_current_object()
->method2_returning_ptr_to_current_object()
;
std::cout << "cascading &address-of, ptr, ptr\n";
(&object)
->method1_returning_ptr_to_current_object()
->method2_returning_ptr_to_current_object()
;
std::cout << "cascading new ptr ref ptr ref\n";
My_Object* object_ptr2
= (&(*(new My_Object)
->method1_returning_ptr_to_current_object())
.method2_returning_ref_to_current_object())
;
}
Test output
cascading ref, ref, copy, copy, ref, ref
1: method1_returning_ref_to_current_object
1: method2_returning_ref_to_current_object
1: method1_returning_value_copy_of_current_object
2: method2_returning_value_copy_of_current_object
3: method1_returning_ref_to_current_object
3: method2_returning_ref_to_current_object
cascading ptr, ptr, ptr, ptr
4: method1_returning_ptr_to_current_object
4: method2_returning_ptr_to_current_object
4: method1_returning_ptr_to_current_object
4: method2_returning_ptr_to_current_object
cascading &address-of, ptr, ptr
4: method1_returning_ptr_to_current_object
4: method2_returning_ptr_to_current_object
cascading new ptr ref ptr ref
5: method1_returning_ptr_to_current_object
5: method2_returning_ref_to_current_object
By the way, I do NOT need debugging help here. I provide the examples only for clarity.
I am seeking style advice.
Everyone has their own style; as you say, it only really gets annoying when you start mixing them.
Personally, I only return a pointer from a function if it might be 0; this is never 0, so I would always return *this (i.e. a reference) and thus chain with ..
For what it's worth, I also try really hard to make the default constructor cheap, partly because there are so many cases in which it turns out to be convenient to first default construct and then assign.
The best answer I can give is "be consistent". If the rest of your code uses this->, use that. If it uses (*this)., use that.
Since the difference is only syntactic sugar, your best guidance is what the other code you use does. Most people I know would prefer the -> syntax, but if you're integrating into existing libraries, you may want to skip it.
Personally, I'd use the extra-line method of initialization. It reads the cleanest to me, one line constructs the object on the stack, the others call the methods as needed. If you only need the methods and they're not dependent on the actual object, I'd make them static and skip object creation all together.
Related
I have written a simple c++ code to understand the concepts of Copy Constructor/Operator Overloading. A snippet of the code is shown below.
In the code I am creating an object vec v2 and then creating a new object v4 and assign vec v2. Next I called the overloaded operator[] to change the values of v4[0] and v4[1].
My issue is that after assigning these values, the values of vec v2 has also changed.
I am not quite sure how this could have happened. Hope anyone can help me with this.
class vec {
private:
// Variable to store the number of elements contained in this vec.
size_t elements;
// Pointer to store the address of the dynamically allocated memory.
double* data;
public:
vec(size_t size) : elements{ size }, data{ new double[size] } {std::cout << "First constructor" << "\n"; };
vec(size_t size, double ival) : elements{ size }, data{ new double[size] } {
std::cout << "Second constructor" << std::endl;
for (int i = 0; i < elements; i++) {
data[i] = ival;
}
}
vec(std::initializer_list<double> iList): vec(static_cast<size_t>(iList.size())) {
std::cout << "Third constructor" << std::endl;
auto count{ 0 };
for (auto element: iList) {
data[count] = element;
count++;
}
}
/// Copy constructor that creates a copy of the vec variable 'v'.
vec(const vec& v) : elements{ v.elements }, data{ new double[v.elements] }{
std::cout << "Copy constructor " << "\n";
std::memcpy(&data, &(v.data), v.elements);
}
/// Copy assignment operator. Creates a copy of vec variable 'v'.
vec& operator=(const vec& v) {
std::cout << "Copy assignment " << "\n";
if (this != &v) {
const auto new_data{ new double[v.elements] };
delete[] data;
data = new_data;
elements = v.elements;
std::memcpy(&data, &(v.data), v.elements);
}
return *this;
}
double& operator[](size_t idx){
return this->data[idx];
}
friend std::ostream& operator<<(std::ostream& os, const vec& v) {
for (int i = 0; i < v.elements; i++) {
os << v.data[i] << "\n";
}
return os;
}
};
int main(void) {
vec v2 = {4, 5, 6};
vec v4 = v2
v4[0] = 11; // call to operator[]
v4[1] = 12; // call to operator[]
return 0;
}
The issue is the misuse of the std::memcpy function:
std::memcpy(&data, &(v.data), v.elements);
Since data and v.data are already pointers to the data, getting the address of those pointers results in the incorrect pointer values being used for those arguments.
The other issue is that the third argument v.elements should denote the number of bytes, not the number of elements, to copy.
The correct call to std::memcpy should have been:
std::memcpy(data, v.data, v.elements * sizeof(double));
But having said all this, do not use std::memcpy, and instead use std::copy. That will work with the number of elements, plus can work with types that are not trivially-copyable (such as std::string):
#include <algorithm>
//...
std::copy(v.data, v.data + v.elements, data);
I want to a map container that takes a smart pointer to a custom type. When you put the key in, you get shared_ptrs to the object, but when all of those shared_ptrs go out of scope, the object is destroyed inside the map (in contrast to a normal map, where the object persists until the map goes out of scope or until it is explicitly deleted).
Here's what I want to achieve:
SpecialMap<int, std::string> map;
{
std::shared_ptr<std::string> item = map.getElement(3);
} // element at 3 is deleted *inside the map* here, because the pointer goes out of scope.
assert(map.getSize() == 0);
I think that this can be done by giving the map a member type of std::weak_ptr, which doesn't increment the reference count, and then using a std::shared_ptr with a custom deleter which removes the element from the map, rather than calling delete. But I'm struggling with how to insert items to the map. Here's what I'm trying right now.
(In this example I'm just using int and string to keep things simple--I'll template it properly later on).
class SpecialMap
{
public:
std::shared_ptr<std::string> getElement(int i)
{
auto result = map.try_emplace(i, std::shared_ptr<std::string>(new std::string("text"),
[this](std::string* s) // here is the custom deleter
{
for (auto it = map.begin(); it != map.end();)
{
if (&*it->second.lock() == s)
{
map.erase(it);
return;
}
else ++it;
}
}));
return result.first->second.lock();
}
int getSize() const
{ return map.size(); }
private:
std::unordered_map<int, std::weak_ptr<std::string>> map;
};
This code doesn't work because the shared_ptr inside try_emplace goes out of scope immediately, deleting the item before it can be returned.
Can anyone suggest a better way of interacting with unordered_map here? Or is there a better way to approach the problem?
Consider the following part of the deleter...
for (auto it = map.begin(); it != map.end();) {
if (&*it->second.lock() == s) {
map.erase(it);
return;
}
else
++it;
}
But the deleter is only being called because the ref count has become zero. That being the case the call...
it->second.lock()
will return a shared pointer that refers to a null pointer. Hence the equality will never hold and the corresponding element in the unordered_map will never be erased.
The usual way to deal with something like this would be to have something like...
using container_type = std::unordered_map<int, std::weak_ptr<foo>>;
container_type m_map;
std::map<foo *, container_type::const_iterator> m_iters;
Here m_map is the real data store and m_iters can be used to find the appropriate iterator to erase when the shared pointers ref count becomes zero and the deleter is called.
class special_map {
using container_type = std::unordered_map<int, std::weak_ptr<std::string>>;
public:
using const_iterator = container_type::const_iterator;
std::shared_ptr<std::string> get_element (int i)
{
/*
* Check to see if the requested element already exists. If so simply
* return it.
*/
auto iter = m_map.find(i);
if (iter != m_map.end())
return iter->second.lock();
std::shared_ptr<std::string> value(
new std::string,
[this](std::string *s)
{
std::cout << "deleter called on " << s << "\n";
if (auto i = m_iters.find(s); i != m_iters.end()) {
m_map.erase(i->second);
m_iters.erase(i);
}
delete s;
});
iter = m_map.emplace(i, value).first;
auto sp = iter->second.lock();
m_iters[sp.get()] = iter;
return sp;
}
int get_size() const
{
return m_map.size();
}
const_iterator begin () const
{
return m_map.begin();
}
const_iterator end () const
{
return m_map.end();
}
private:
container_type m_map;
std::map<std::string *, container_type::const_iterator> m_iters;
};
std::ostream &operator<< (std::ostream &os, const special_map &v)
{
std::cout << "special_map#" << &v << "(" << v.get_size() << " elements)...\n";
for (const auto &i: v) {
auto sp = i.second.lock();
std::cout << "[" << i.first << "] --> [std::string#" << sp.get() << "/" << (sp.use_count() - 1) << "]\n";
}
return os << "\n";
}
int main ()
{
{
special_map sm;
{
auto s1 = sm.get_element(5);
std::cout << sm;
{
auto s2 = sm.get_element(5);
std::cout << sm;
}
std::cout << sm;
}
std::cout << sm;
}
}
Example output from this is...
special_map#0x7ffedf44d6a0(1 elements)...
[5] --> [std::string#0xf75bc0/1]
special_map#0x7ffedf44d6a0(1 elements)...
[5] --> [std::string#0xf75bc0/2]
special_map#0x7ffedf44d6a0(1 elements)...
[5] --> [std::string#0xf75bc0/1]
deleter called on 0xf75bc0
special_map#0x7ffedf44d6a0(0 elements)...
Edit 1
If the key type being used is sufficiently 'lightweight' as with int in the current case then it might be possible to avoid the use of the extra m_iters container by having the deleter lambda capture the key by value and performing the map lookup based on that. In that case the code becomes...
class special_map {
using container_type = std::unordered_map<int, std::weak_ptr<std::string>>;
public:
using const_iterator = container_type::const_iterator;
std::shared_ptr<std::string> get_element (int i)
{
/*
* Check to see if the requested element already exists. If so simply
* return it.
*/
auto iter = m_map.find(i);
if (iter != m_map.end())
return iter->second.lock();
std::shared_ptr<std::string> value(
new std::string,
[this, i](std::string *s)
{
std::cout << "deleter called on " << s << "\n";
m_map.erase(i);
delete s;
}
);
return m_map.emplace(i, value).first->second.lock();
}
int get_size() const
{
return m_map.size();
}
const_iterator begin () const
{
return m_map.begin();
}
const_iterator end () const
{
return m_map.end();
}
private:
container_type m_map;
};
First of all, there is memory leak in your code, becouse there is no delete in your custom delter object which receives pointer, so I added to your code delete operator.
And As good as I understand you, you should store your pointer into variable first, and then try to emplace it into your map, so it should looks like this
class SpecialMap
{
public:
std::shared_ptr<std::string> getElement(int i)
{
auto ptr = std::shared_ptr<std::string>(new std::string("Hello"),
[this](std::string* s) // here is the custom deleter
{
std::cout << "custom deleter is called\n";
for (auto it = map.begin(); it != map.end();)
{
if (&*it->second.lock() == s)
{
map.erase(it);
return;
}
else ++it;
}
delete s;
});
auto result = map.try_emplace(i, ptr);
return result.first->second.lock();
}
int getSize() const
{ return map.size(); }
private:
std::unordered_map<int, std::weak_ptr<std::string>> map;
};
int main()
{
SpecialMap map;
{
std::shared_ptr<std::string> item = map.getElement(3);
std::cout << "some work with your ptr\n";
} // element at 3 is deleted *inside the map* here, because the pointer goes out of scope.
}
And the output should be like this
some work with your ptr
custom deleter is called
Here is some live example with custom class
Dear Stackoverflow community,
I'm still bit fresh in c++ and I've been scratching my head and haven't found a solution to my problem yet. I've been searching and trying things for a while now and I've gotten to the point where asking a question would be more beneficial and educational.
Problem:
I'd like to make a class or function that wraps/decorates a given function with or without parameters.
Like a good old fashioned #wrapthis in python or c# and the like.
The closest thing I found so far (that is elegant, short and easy to use) is from this stackoverflow answer: C++ Equivalent Decorator
The scratching-my-head part is trying to pass a pointer-function. The error I'm receiving:
Error (active) E0300 a pointer to a bound function may only be used to call the function
Which obviously means that somehow passing a pointer in this fashion is not allowed, so what are my options here?
A working example as answer would be great!
Example of what I'm trying to achieve can be found below:
Something.h
class Something
{
private:
public:
Something() {}
void v_func_with_nothing() { std::cout << "v_func_with_nothing" << "\n"; }
void v_func_with_void(void) { std::cout << "v_func_with_void" << "\n"; }
void v_func_with_one_arg(int x) { std::cout << "v_func_with_one_arg" << x << " " << "\n"; }
void v_func_with_args(int x, int y) { std::cout << "v_func_with_args" << x << " " << y << "\n"; }
int return_func_with_nothing() { return 1; }
int return_func_with_void(void) { return 3; }
int return_func_with_one_arg(int x) { return x; }
int return_func_with_args(int x, int y) { return x+y; }
};
Decorator.h [Again source: C++ Equivalent Decorator]
template<typename T>
auto decorator(T&& func)
{
auto new_function = [func = std::forward<T>(func)](auto&&... args)
{
std::cout << "BEGIN decorating...\n";
auto result = func(std::forward<decltype(args)>(args)...);
std::cout << "END decorating\n";
return result;
};
return new_function;
}
main.cpp
#include <iostream>
#include "Something.h"
#include "Decorator.h"
int main()
{
Something* something = new Something();
auto somedeco = decorator(&something->return_func_with_one_arg);//<-- error here in argument
//int value = somedeco(**enter an argument**);
//std::cout << value << "\n";
return 0;
}
Thank you!
EDIT: SOLUTION
Based on the kind answers given down below I thought of editing this post with an example. The solution to the problem was using lambda.
Decorator.h: I created 2 decorators (one for return-functions, one for void-functions):
template<typename T>
auto DECO_R(T&& func)
{
try
{
auto new_function = [func = std::forward<T>(func)](auto&&... args)
{
std::cout << "BEGIN RETURN decorating...\n";
auto result = func(std::forward<decltype(args)>(args)...);
std::cout << "END RETURN decorating\n";
return result;
};
return new_function;
}
catch (const std::exception& ex)
{
std::cout << ex.what() << "\n";
}
}
template<typename T>
auto DECO_V(T&& func)
{
try
{
auto new_function = [func = std::forward<T>(func)](auto&&... args)
{
std::cout << "BEGIN VOID decorating...\n";
func(std::forward<decltype(args)>(args)...);
std::cout << "END VOID decorating\n";
};
return new_function;
}
catch (const std::exception& ex)
{
std::cout << ex.what() << "\n";
}
}
Main.cpp: 2 examples
int main()
{
Something* something = new Something();
auto somedeco = DECO_R(
[&](int x) {
return something->return_func_with_one_arg(x);
});
int value = somedeco(255);
std::cout << value << "\n";
auto some_v_deco = DECO_V(
[&](int x) {
return something->v_func_with_one_arg(x);
});
some_v_deco(2);
return 0;
}
Output
BEGIN RETURN decorating...
END RETURN decorating
255
BEGIN VOID decorating...
v_func_with_one_arg2
END VOID decorating
I hope this helps others out there.
The call decorator(&something->return_func_with_one_arg) is invalid. There's no such thing as a pointer to a bound function in C++.
If you want somedeco to be a function-like object that wraps a call to something->return_func_with_one_arg(42), for example, you will need to wrap the call either in a lambda:
auto somedeco = decorator(
[&]() {
return something->return_func_with_one_arg(42);
}
);
somedeco();
Or you could pass the parameter through the decorator:
auto somedeco = decorator(
[&](int x) {
return something->return_func_with_one_arg(x);
}
);
somedeco(42);
Keep in mind that this will require that the object pointed to by something outlives the object returned by decorator.
There is no simple 1:1 replacement for Pythons dynamic typing in C++. Your example does not compile, because there are no pointer to member function of one specific instance. Pointer to member functions always need an instance to be called. For one simple case I would suggest to use a lambda:
int main() {
Something something;
auto somedeco = [&](auto param) {
// before call
auto v = something.return_func_with_one_arg(param);
// after call
return v;
};
return somedeco(1);
}
As you can see the whole machinery of decorate isn't really needed, because with a lamdda you can write the wrapped function inline. On the other hand, the decorator allows you to reuse // before call and // after call for different methods. To fix your code you could also pass the lambda to decorate.
PS: Don't use new to create objects.
you need bind it
int main()
{
Something* something = new Something();
using std::placeholders::_1;
auto f = std::bind( &Something::return_func_with_one_arg, something, _1 );
auto somedeco = decorator( f );//<-- error here in argument
//int value = somedeco(**enter an argument**);
//std::cout << value << "\n";
return 0;
}
https://godbolt.org/z/zdYW9q
Without polymorphism
I have implemented an entity-component system that useses templates to get the components. An id is generated for each type. The function size_t GetComponentTypeId<T>() will always return the same id for a given type T.
For better understanding, here the function to add components
template <typename TComponent, typename... TArguments>
inline TComponent & Entity::AddComponent(TArguments&&... arguments)
{
// Check whether the component doesn't already exist
assert(componentBitSet[detail::GetComponentTypeID<TComponent>()] == false && "The component already exists");
assert(componentArray[detail::GetComponentTypeID<TComponent>()] == nullptr && "The component already exists");
TComponent * c = new TComponent(*this, std::forward<TArguments>(arguments)...);
Component::UPtr uPtr{ c };
componentList.emplace_back(std::move(uPtr));
// set the component * in the array
componentArray[detail::GetComponentTypeID<TComponent>()] = c;
// set the according component flag to true
componentBitSet[detail::GetComponentTypeID<TComponent>()] = true;
return *c;
}
And here the function to get components
template<typename TComponent>
inline TComponent & Entity::GetComponent() const
{
Component * component = componentArray[getComponentTypeID<TComponent>()];
if (component == nullptr)
throw std::runtime_error("Entity: This entity does not have the requested component");
return *static_cast<TComponent*>(component);
}
Nothing special here
Also my current implementation if the GetComponentTypeID() method:
namespace detail
{
typedef std::size_t ComponentTypeID;
/// #brief Returns a unique number (for each function call) of type std::size_t
inline ComponentTypeID GetComponentID() noexcept
{
// This will only be initialised once
static ComponentTypeID lastId = 0;
// After the first initialisation a new number will be returned for every function call
return lastId++;
}
/// #brief Returns a unique number (of type std::size_t) for each type T
/// #details Each component type will have its own unique id.
/// The id will be the same for every instance of that type
/// #tparam T The type for which the id is generated
template <typename T>
inline ComponentTypeID GetComponentTypeID() noexcept
{
// There will be only one static variable for each template type
static ComponentTypeID typeId = GetComponentID();
return typeId;
}
} // namespace detail
Adding polymorphism
Now I want to add polymorphic behaivour to my classes. E.g. there might be a SpriteRenderComponent which inherits from RenderComponent (which of course inherits Component). The RenderComponent would have a virtual draw method that is implemented in the SpriteRenderComponent. I want to be able to only add the sprite component and still be able to get the a reference to the renderComponent by calling entity.GetComponent<RenderComponent>() on the entity to which the sprite component has been added to. Calling the draw method on the returned render component reference should call SpriteRenderComponent.draw(). Also I should not be able to add any other components that inherit from render component.
Some of my thoughts
I think, the basic solution would be to add a pointer of the one instance of the SpriteRenderComponent for both ids; RenderComponent and SpriteRenderComponent. This would also prevent the user from adding multiple components that inherit from RenderComponent. The component itself would only be added once to the componentList and so only be updated once per frame (as wanted)
Problem: Making it typesafe
My problem is that I am struggeling to make it typesafe. Also I want to include some kind of check that makes sure that SpriteRenderComponent actually inherits from RenderComponent. My favourite solution would be one that 'automatically adds gets the superclass's ids and adds the component pointer for them as well. I am quite new to this kind of meta programming (perhaps the wrong word) so help would be much appreciated.
Update
One possible solution I found was to add an AddPolymorphism<class TDerivedComponent, class TBaseComponent>() method to the entity class. Here is the implementation:
template<class TDerivedComponent, class TBaseComponent>
inline void Entity::AddPolymorphism()
{
// Needed since std::is_base_of<T, T> == true
static_assert(std::is_base_of<Component, TBaseComponent>::value, "Entity: TBaseComponent must inherit from Component");
static_assert(std::is_same<Component, TBaseComponent>::value == false, "Entity: TBaseComponent must inherit from Component");
static_assert(std::is_base_of<TBaseComponent, TDerivedComponent>::value, "Entity: TDerivedComponent must inherit from TBaseComponent");
static_assert(std::is_same<Component, TBaseComponent>::value == false, "Entity: TBaseComponent must inherit from Component");
assert(this->HasComponent<TDerivedComponent>() && "Entity: The entity must have the derived component");
auto derivedComponentPtr = componentDictionary.find(detail::GetComponentTypeID<TDerivedComponent>())->second.lock();
componentDictionary.insert(std::make_pair(detail::GetComponentTypeID<TBaseComponent>(), derivedComponentPtr));
}
I guess its kinda typesafe but for me it has one major issue. It requires me to call this function every single time I add an component that has polymorphic behaivour. Although this is a solution (kinda) I'd much prefer a static way to specify this behaivour.
For the part about making sure it inherits from:
template<typename T>
struct Foo {
static_assert(is_base_of<Base, T>::value, "T must inherit from Base");
};
Might help you out there; as for the other questions; I will need more time as I have to soon leave... I'll come back to this later on when I get the chance to update this answer.
EDIT - Added some additional classes and show their uses.
I've had some time to work on something; I don't know for sure if this is what you are looking for; but this is a storage-manager type system that I've used before. It does support polymorphic behavior of classes. So maybe this structure will help you out.
main.cpp
#include <iostream>
#include <string>
#include <memory>
#include "FooManager.h"
#include "DerivedFoos.h"
int main() {
try {
std::unique_ptr<FooManager> pFooManager;
pFooManager.reset( new FooManager() );
for ( unsigned i = 0; i < 10; i++ ) {
DerivedA* pA = new DerivedA();
DerivedB* pB = new DerivedB();
pFooManager->add( pA, FOO_A );
pFooManager->add( pB, FOO_B );
}
pFooManager.reset();
} catch ( std::exception& e ) {
std::cout << e.what() << std::endl;
std::cout << "\nPress any key to quit.\n";
std::cin.get();
return -1;
} catch ( std::string str ) {
std::cout << str << std::endl;
std::cout << "\nPress any key to quit.\n";
std::cin.get();
return -1;
} catch ( ... ) {
std::cout << __FUNCTION__ << " caught unknown exception." << std::endl;
std::cout << "\nPress any key to quit.\n";
std::cin.get();
return -1;
}
std::cout << "\nPress any key to quit.\n";
std::cin.get();
return 0;
}
FooBase.h
#ifndef FOO_BASE_H
#define FOO_BASE_H
enum FooTypes {
FOO_A,
FOO_B,
FOO_UNKNOWN // MUST BE LAST!!!
};
class FooBase {
protected:
std::string _nameAndId;
private:
std::string _id;
static int _baseCounter;
public:
std::string idOfBase();
virtual std::string idOf() const = 0;
protected:
FooBase();
};
#endif // !FOO_BASE_H
FooBase.cpp
#include "FooBase.h"
#include <iostream>
#include <string>
int FooBase::_baseCounter = 0;
FooBase::FooBase() {
_id = std::string( __FUNCTION__ ) + std::to_string( ++_baseCounter );
std::cout << _id << " was created." << std::endl;
}
std::string FooBase::idOfBase() {
return _id;
}
std::string FooBase::idOf() const {
return "";
} // empty
DerivedFoos.h
#ifndef DERIVED_FOOS_H
#define DERIVED_FOOS_H
#include "FooBase.h"
class DerivedA : public FooBase {
private:
static int _derivedCounter;
public:
DerivedA();
std::string idOf() const override;
};
class DerivedB : public FooBase {
private:
static int _derivedCounter;
public:
DerivedB();
std::string idOf() const override;
};
#endif // !DERIVED_FOOS_H
DerivedFoos.cpp
#include "DerivedFoos.h"
#include <iostream>
#include <string>
int DerivedA::_derivedCounter = 0;
int DerivedB::_derivedCounter = 0;
DerivedA::DerivedA() : FooBase() {
_nameAndId = std::string( __FUNCTION__ ) + std::to_string( ++DerivedA::_derivedCounter );
std::cout << _nameAndId << " was created." << std::endl;
}
std::string DerivedA::idOf() const {
return _nameAndId;
}
DerivedB::DerivedB() : FooBase() {
_nameAndId = std::string( __FUNCTION__ ) + std::to_string( ++DerivedB::_derivedCounter );
std::cout << _nameAndId << " was created." << std::endl;
}
std::string DerivedB::idOf() const {
return _nameAndId;
}
FooManager.h - I'm not going to change the code for this class to replace its name. After looking at this for a little while; it became apparent that something like FooStore or Storage etc. would be a more suitable name for this class. It doesn't really manage anything other than the adding and removing of objects from its member container(s). You could keep its name as is if you decide to add more functionality that does more than just the add and remove objects.
#ifndef FOO_MANAGER_H
#define FOO_MANAGER_H
class FooBase;
class DerivedA;
class DerivedB;
enum FooTypes;
class FooManager final {
private:
static bool _alreadyExists;
typedef std::unordered_map<std::string, std::shared_ptr<FooBase>> MapFoos;
MapFoos _idsA;
MapFoos _idsB;
std::vector<std::string> _foosForRemoval;
public:
FooManager();
~FooManager();
// Foo Objects
FooBase* getFoo( const std::string& id, FooTypes type ) const;
void add( FooBase* foo, FooTypes type );
bool removeFoo( const std::string& id );
template<typename T>
bool removeFoo( T* pFoo );
void markFooForRemoval( const std::string& id );
private:
FooBase* getFoo( const std::string& id, const MapFoos& fooMap ) const;
void add( FooBase* pFoo, MapFoos& fooMap );
bool removeFoo( const std::string& strId, MapFoos& fooMap );
};
template<typename T>
inline bool FooManager::removeFoo( T* pFoo ) {
return false;
}
#endif // !FOO_MANAGER_H
FooManager.cpp
#include "FooManager.h"
#include "DerivedFoos.h"
#include <iostream>
#include <sstream>
#include <string>
#include <unordered_map>
#include <memory>
bool FooManager::_alreadyExists = false;
FooManager::FooManager() {
// First check if no other instance is created.
if ( _alreadyExists ) {
std::ostringstream strStream;
strStream << "Failed to create " << __FUNCTION__ << " as it was already created." << std::endl;
throw strStream.str();
}
// Make sure this is last
_alreadyExists = true;
std::cout << __FUNCTION__ + std::string( " was created successfully." ) << std::endl;
}
FooManager::~FooManager() {
// If we are destroying make sure to reset flag
// So it can be constructed again.
_idsA.clear();
_idsB.clear();
_alreadyExists = false;
std::cout << __FUNCTION__ + std::string( " was destroyed successfully." ) << std::endl;
}
FooBase* FooManager::getFoo( const std::string& id, FooTypes type ) const {
switch ( type ) {
case FOO_A: {
return getFoo( id, _idsA );
}
case FOO_B: {
return getFoo( id, _idsB );
}
default: {
std::ostringstream strStream;
strStream << __FUNCTION__ << " Unrecognized FooType = " << type;
throw strStream.str();
}
}
return nullptr;
}
FooBase* FooManager::getFoo( const std::string& id, const MapFoos& fooMap ) const {
MapFoos::const_iterator itFoo = fooMap.find( id );
if ( itFoo == fooMap.cend() ) {
return nullptr;
}
return itFoo->second.get();
}
void FooManager::add( FooBase* pFoo, FooTypes type ) {
// first check to see foo is valid
if ( nullptr == pFoo ) {
std::ostringstream strStream;
strStream << __FUNCTION__ + std::string( " pFoo == nullptr passed in" );
}
// Make Sure Name Is Unique Across All Foo Types
for ( int i = 0; i < FOO_UNKNOWN; ++i ) {
if ( getFoo( pFoo->idOf(), (FooTypes)i ) != nullptr ) {
std::ostringstream strStream;
strStream << __FUNCTION__ << " attempting to store " << pFoo->idOf() << " multiple times" << std::endl;
throw strStream.str();
}
}
switch ( type ) {
case FOO_A: {
add( pFoo, _idsA );
break;
}
case FOO_B: {
add( pFoo, _idsB );
break;
}
default: {
std::ostringstream strStream;
strStream << __FUNCTION__ << " uncrecognized FooType = " << type;
}
}
}
void FooManager::add( FooBase* pFoo, MapFoos& fooMap ) {
fooMap.insert( MapFoos::value_type( pFoo->idOf(), std::shared_ptr<FooBase>( pFoo ) ) );
}
template<>
bool FooManager::removeFoo( DerivedA* pFoo ) {
return removeFoo( pFoo->idOf(), _idsA );
}
template<>
bool FooManager::removeFoo( DerivedB* pFoo ) {
return removeFoo( pFoo->idOf(), _idsB );
}
bool FooManager::removeFoo( const std::string& id ) {
// Find which type this Foo is in
for ( int i = 0; i < FOO_UNKNOWN; ++i ) {
FooBase* pFoo = getFoo( id, (FooTypes)i );
if ( pFoo != nullptr ) {
// Found It
switch ( static_cast<FooTypes>(i) ) {
case FOO_A: {
return removeFoo( pFoo->idOf(), _idsA );
}
case FOO_B: {
return removeFoo( pFoo->idOf(), _idsB );
}
default: {
std::ostringstream strStream;
strStream << __FUNCTION__ << " uncrecognized FooType = " << i;
throw strStream.str();
}
}
}
}
std::ostringstream strStream;
strStream << __FUNCTION__ << " failed. " << id << " was not found in FooManager";
// don't throw just display message (typically write to log file).
std::cout << strStream.str() << std::endl;
return false;
}
bool FooManager::removeFoo( const std::string& id, MapFoos& fooMap ) {
MapFoos::iterator itFoo = fooMap.find( id );
if ( itFoo == fooMap.end() ) {
std::ostringstream strStream;
strStream << __FUNCTION__ << " failed. " << id << " was not found in AssetStorage";
// don't throw just display message (typically write to log file).
std::cout << strStream.str() << std::endl;
return false;
} else {
// do what ever from Foo's functions to clean up its internals
// itFoo->second.get()->cleanUp(); // etc.
fooMap.erase( itFoo );
// When the above foo was deleted, there might have been some children
// that were also marked for removal. We can remove them here.
for ( unsigned i = 0; i < _foosForRemoval.size(); ++i ) {
itFoo = _idsB.find( _foosForRemoval[i] );
if ( itFoo != _idsB.end() ) {
// Remove this Foo
// do what ever from Foo's functions to clean up its internals.
// itFoo->second.get()->cleanUp(); // etc.
_idsB.erase( itFoo );
} else {
std::ostringstream strStream;
strStream << __FUNCTION__ << " failed to find " << _foosForRemoval[i] << " for removal from the _idsB";
// don't throw just display message (typically write to log file).
std::cout << strStream.str() << std::endl;
}
}
_foosForRemoval.clear();
return true;
}
}
void FooManager::markFooForRemoval( const std::string& id ) {
_foosForRemoval.push_back( id );
}
It's a nice way to store items dynamically and yes you can see that I'm using new on the pointers in main, but you never see me using delete. This is because once we add that pointer to the manager class, it takes over and handles all the memory for us, since it will turn them into shared_ptr<T>. This manager class also supports polymorphic behavior. This is just a basic shell or structure.
Then from here. You can write another class that holds a pointer to this storage or manager class that will then add and remove items from these containers. The other class would be responsible for finding the objects in this storage and then invoking the methods of the internally stored objects; or you could just add all of that functionality directly into this class. I kind of like to try and keep the storage of things separate from the implementation of things. I hope this structure helps you out, or gives you some ideas to work off of. You can see that I did use function templates within this class to access specific maps of specific derived foos.
You should be able to integrate into the above classes the concept of the is_derived_from as well as checking to see if a specific item already exists and if does don't add it. Final note: you could also split the storage into 2 types where one container will be able to add multiple components that can be rendered multiple times per frame, while the other container can be restrictive. Not sure what kind of benefit you could get with that, maybe in a particle generator or engine system, but the flexibility is there to do that if you need it.
You just need to make detail::GetComponentTypeID<T>() smarter.
You have, in practice, a list of component types.
template<class...>
struct type_list_t {};
using ComponentList = type_list_t<RenderComponent, PhysicsComponent, CharmComponent>;
this list determines how long your pointer and bit flag arrays are. Place this list explicitly in a famous location that everyone knows about.
Yes, this means you have to rebuild if it changes. Tough.
Now you just have to improve detail::GetComponentTypeID<T>(). Have it constexpr or template metaprogramming search the ComponentList for the first type that passes std::is_base_of< ListElement, T >.
And your code now works as written.
I would like to write a wrapper class with all operators overloaded such that I can detect when we write/read or modify its contents. For instance:
probe<int> x;
x = 5; // write
if(x) { // read
x += 7; // modify
}
Anyone already did that? If not which operators must I overload to be sure I dont miss anything?
Use this as a common idea.
There are plenty of operators like &= |= [] which maybe are not principal in your case.
template < typename T >
struct monitor
{
monitor( const T& data ):
data_( data )
{
id_ = get_next_monitor_id();
}
monitor( const monitor& m )
{
id_ = get_next_monitor_id();
m.notify_read();
notify_write();
data_ = m.data_;
}
operator T()
{
notify_read();
return data_;
}
monitor& operator = ( const monitor& m )
{
m.notify_read();
notify_write();
data_ = m.data_;
return *this;
}
monitor& operator += ( const monitor& m )
{
m.notify_read();
notify_write();
data_ += m.data_;
return *this;
}
/*
operator *=
operator /=
operator ++ ();
operator ++ (int);
operator -- ();
operator -- (int);
*/
private:
int id_;
T data_;
void notify_read()
{
std::cout << "object " << id_ << " was read" << std::endl;
}
void notify_write()
{
std::cout << "object " << id_ << " was written" << std::endl;
}
};
You can't, I think. operator?: isn't overloadable. Also, if T::T(int) is defined, T foo = 4 is legal but T foo = probe<int>(4) isn't. There's at most one user-defined conversion.
Furthermore, because probe is not a POD, the behavior of your program can change.