How to map different C++ classes to enum class values - c++

I produce messages and each is receive by one object, chosen by an enum class member:
enum class ReceiverID
{
R1,
R2,
MAX_NUM_RECEIVERS
};
struct Msg
{
ReceiverID _receiverID;
Data _data;
};
The receiving classes are stored in an array. The enum member indexes the array to access the receiving object:
void receive(const Msg& msg)
{
const size_t arrIndex = static_cast<size_t>(msg._receiverID);
if(nullptr == _array[arrIndex])
{
_array[arrIndex] = ??? // How do I associate the enum to the class?
}
_array[arrIndex].processMsg(msg);
}
It is possible the receiving object is missing. If this happens I'd like to use the enum to instantiate the missing object. However, this would require mapping the enum values to the receiving object type.
How can I map a class to each enum? (for all enums).
I'd like to generate a compiler error if a new enum is added but without a corresponding receiver class.
UPDATE
The receiving objects are polymorphic and so have a base class. The array is:
std::array<Base*, MAX_NUM_RECEIVERS> _array;
(removed unique_ptr to simplify question)

For on-the-fly creation of objects we could go for some kind of a factory method, e.g.:
//In the Base class:
static Base* createReceiver(ReceiverID recvID) //static member function
{
switch (recvID)
{
case ReceiverID::R1: return new R1Receiver();
case ReceiverID::R2: return new R2Receiver();
//...
default: throw std::logic_error("Invalid ReceiverID");
}
}
//...
void receive(const Msg& msg) except(true)
{
const size_t arrIndex = static_cast<size_t>(msg._receiverID);
if(nullptr == _array[arrIndex])
{
_array[arrIndex] = Base::createReceiver(msg._receiverID);
}
_array[arrIndex]->processMsg(msg);
}

Instead of having a global std::array<Base*, MAX_NUM_RECEIVERS> _array; and then lazily filling it out on demand, I believe the normal thing to do is make it filled out at construction time:
std::array<Base*, MAX_NUM_RECEIVERS>& _array() {
//use a method to bypass https://stackoverflow.com/questions/1005685/c-static-initialization-order
static std::array<Base*, MAX_NUM_RECEIVERS> array = make_array();
return array;
}
std::array<Base*, MAX_NUM_RECEIVERS> make_array() {
std::array<Base*, MAX_NUM_RECEIVERS> array;
array[static_cast<size_t>(R1)] = &myR1ProcessorObject();
array[static_cast<size_t>(R2)] = &myR2ProcessorObject();
return array;
}
Then your receive method is simple:
void receive(const Msg& msg)
{
const size_t arrIndex = static_cast<size_t>(msg._receiverID);
assert(arrIndex< MAX_NUM_RECEIVERS);
_array()[arrIndex].processMsg(msg);
}

Related

How to register an object using string or int as key dynamically, and store, access it with the key in C++?

I want to design a data dashboard framework with c++ with the following requirements:
clients can register data type to the dashboard using a key (string or integer are both acceptable)
clients can store/access the object with the known data type and key
The following example code demonstrates how I want to use it
// data.hpp
struct DataA
{
DataA();
int a;
};
struct DataF
{
DataF();
float f;
};
// data.cpp
static DataA s_da;
static DataF s_df;
DataA::DataA()
{
if(!Dashboard::Register("A", s_da)) {
cerr << "unable to register DataA, key is used";
}
}
DataF::DataF()
{
if(!Dashboard::Register("F", s_df)) {
cerr << "unable to register DataF, key is used";
}
}
// main.cpp
int main ()
{
DataA da;
da.a = 123;
Dashboard::Set("A", da);
DataF df;
df.f = 3.14;
Dashboard::Set("F", df);
cout << ((DataA)Dashboard::Get("A")).a << endl; // 123
cout << ((DataF)Dashboard::Get("F")).f << endl; // 3.14
return 0;
}
However, I can't come up with any idea to implement the Dashboard class to provide the interface.
How could I dynamically register an object with a given datatype and key? Is there any design pattern that addresses this requirement?
I built something like you are describing on my team as an internal for a library that was used for routing messages across the application.
If you are stuck on C++11 (such that std::any or std::variant is unavailable), you could have an empty virtual base class type as follows:
class BaseValue
{
public:
BaseValue() {};
virtual ~BaseValue() {}; // need one virtual method for dynamic_cast
};
And a derived template class such as the following:
template <typename T>
class Value : public BaseValue
{
public:
Value(const T& t) : _t(t)
{}
T _t;
};
Then your data structure is this. It's a map between string to BaseValue pointers
unordered_map<string, BaseValue*> dashboard;
We'll smuggle data into the map above by using the template class Value that derives from BaseValue.
Inserting into the the dashboard is like this:
template <typename T>
void insert(const string& name, const T& t)
{
Value<T>* pT = new Value<T>(t);
dashboard.insert(name, pT);
}
Fetching is something like this. There's different ways to structure a "get" call with respect to "not found" scenarios.
template<typename T>
T& get(const string& name, const T& default = {})
{
auto itor = dashboard.find(name);
if (itor != dashboard.end())
{
BaseValue* pBaseValue = itor->second;
T* pValue = dynamic_cast<T*>(pBaseValue);
if (pValue)
{
return pValue->_t;
}
}
return default; // you could also throw an exception
}
Example insert:
DataA da;
da.a = 123;
insert("a", da);
Example fetch:
DataA& da = get<A>("a");
There's a lot of improvements you can make to the above. For starters, you can convert all of helper functions into a class. Use shared_ptr instead of raw pointers internally. etc...

Initializing member variables with a lambda

Suppose I have a template class:
template<class T>
class Entity
{
public:
Entity(std::function<void(T*)> init, int idx) : index(idx)
{
init(data);
}
T* getData(){ return Data; }
private:
int index;
T* data;
};
And I create an instance of the class as so:
Entity<Button> myEnt([](Button* button){
button = new Button();
/* some complex, **unique**, initialization of button */
}, 1);
This will compile, but when I call getData() and attempt to use the pointer returned in some other function the program crashes. I assume its because there is an error where data doesn't get properly initialized, but I cant tell why!
fwiw I can get the program to run as desired if I change the Entity constructor to:
Entity(std::function<T*(void)> init, int idx) : index(idx)
{
data = init();
}
and then call it as so:
Entity<Button> myEnt([](){
Button* button = new Button();
/* some complex, **unique**, initialization of button */
return button;
}, 1);
But in my opinion that's an undesirable way of doing it, and the first method should work, im just missing something.
You are passing data to init() by value, which means the lambda is receiving a copy of data. So any value the lambda assigns to its input parameter will be assigned to the copy and not reflected back to data.
You need to pass data by reference instead, eg:
template<class T>
class Entity
{
public:
Entity(std::function<void(T*&)> init, int idx) : index(idx)
{
init(data);
}
T* getData(){ return data; }
private:
int index;
T* data;
};
Entity<Button> myEnt([](Button* &button){
button = new Button();
/* some complex, **unique**, initialization of button */
}, 1);
If you want to initialize the pointer member, you need a reference or pointer to the member pointer, not the value of the pointer.
template<class T>
class entity
{
public:
Entity(std::function<void(T**)> init, int idx) : index(idx)
{
init(&data);
}
T* getData(){ return data; }
Private:
int index;
T* data;
}
Use it like this:
Entity<Button> myEnt([](Button** button){
// you need a pointer to the pointer in order to initialize it
*button = new Button();
/* some complex, **unique**, initialization of button */
}, 1);

how to initialize a large size of std::array

i have a class,the class contains a large size of std::array,how to initialize the array??
see class test;
sample:
class context{......}
class value
{
public:
value(context& ctx) : ctx_(ctx){
}
protected:
context& ctx_;
int data_ = 0;
}
class test
{
public:
test() : /*i need to initialize values at here*/ values_{ctx_,.....}
{
}
protected:
context ctx_;
std::array<value_t,10000> values_;
}
in reality,this array maybe only contains 3 or 5 element,not 10000,but someof people definitely gonna give me an answer like below
test() : values_{ctx_,ctx_,ctx_,ctx_,ctx_}
{
}
i don't need a awkward answer like above.
is there a way to initialize std::array with simple code like fold expression???
You can delegate to a constructor that takes a parameter pack then fold over that:
#include <utility>
#include <cstddef>
class test
{
public:
test() : test(std::make_index_sequence<10000>{}) {}
private:
template<std::size_t... I>
test(std::index_sequence<I...>) : values_{{(I, ctx_)...}} {}
protected:
context ctx_;
std::array<value_t, 10000> values_;
};
Though this absolutely killed compile time at any level of optimisation other than -O0 for me (And will probably blow up your compiled code size)
You could also try constructing into uninitialised memory so you don't need to default construct:
#include <array>
#include <cstddef>
#include <new>
#include <memory>
class test
{
public:
test() {
std::byte* p = value_memory_;
for (std::byte* end = std::end(value_memory_); p < end; p += sizeof(value_t)) {
new (p) value_t(ctx_);
}
}
~test() {
value_t* values = get_values();
std::destroy(values, values + 10000);
}
protected:
context ctx_;
value_t* get_values() {
return std::launder(reinterpret_cast<value_t*>(value_memory_));
}
const value_t* get_values() const {
return std::launder(reinterpret_cast<const value_t*>(value_memory_));
}
// These are UB, but work on most compilers, and would generally be nicer
// to work with
value_t(&get_values())[10000] {
return *std::launder(reinterpret_cast<value_t(*)[10000]>(value_memory_));
}
const value_t(&get_values() const)[10000] {
return *std::launder(reinterpret_cast<const value_t(*)[10000]>(value_memory_));
}
private:
alignas(value_t) std::byte value_memory_[sizeof(value_t) * 10000u];
};
Which will have some runtime cost, and you have to lose the std::array (Unless you go for a std::array<std::aligned_storage_t<sizeof(value_t), alignof(value_t)>, 10000>, in which case you have to launder every single element of the array)
The problem is that your array holds elements of a type that does not have a default constructor, so when you declare a std::array holding that type, the array can only be initialized using aggregate initialization so you can explicitly pass in a value to each element's constructor. When the array is a member of a class or struct, that initialization requires use of the class/struct constructor's member initialization list. Exactly what you are trying to avoid.
To get around this, you can use placement-new to explicitly construct each array element individually in a loop:
#include <type_traits>
class context{......}
class value
{
public:
value(context& ctx) : ctx_(ctx){
}
protected:
context& ctx_;
int data_ = 0;
}
class test
{
public:
test()
{
for (auto &v : values_)
new (&v) value(ctx_);
}
~test()
{
for (auto &v : values_) {
// note: needs std::launder in C++17 and later
// std::launder(reinterpret_cast<value*>(&v))->~value();
reinterpret_cast<value*>(&v)->~value();
}
}
protected:
context ctx_;
using storage_type = std::aligned_storage<sizeof(value), alignof(value)>::type;
std::array<storage_type, 10000> values_;
// Access an object in aligned storage
value& operator[](std::size_t pos)
{
// note: needs std::launder in C++17 and later
// return *std::launder(reinterpret_cast<value*>(&values_[pos]));
return *reinterpret_cast<value*>(&values_[pos]);
}
};
You can use fill() method on the array:
https://en.cppreference.com/w/cpp/container/array/fill

How can I have a static function inside a template class?

I'm not very good with C++ but I'm trying to create a generic base class that can be extended to create a kind of dynamic enum. There may be way better ways to do this and I'm open to suggestions but my main question here is why am I getting the error C2338 The C++ Standard doesn't provide a hash for this type.
Base Enums Template Class
template <typename T>
class Enums
{
public:
BOOL HasValue(T enumValue) const
{
auto it = this->m_EnumPairs.find(enumValue);
return (it != this->m_EnumPairs.end());
}
const DWORD Count() const
{
return this->m_EnumPairs.size();
}
const DWORD &operator[](T enumValue) const
{
auto it = this->m_EnumPairs.find(enumValue);
if (it == this->m_EnumPairs.end())
return 0;
return it->second;
}
const DWORD GetVersionCode() const
{
return this->m_VersionCode;
}
static const DWORD Count(DWORD versionCode)
{
T derived(versionCode);
return derived.Count();
}
protected:
DWORD m_VersionCode;
Enums(DWORD versionCode) : m_VersionCode(versionCode), m_NextValue(0) { }
virtual ~Enums() { }
void Add(T enumValue)
{
for (auto it = this->m_EnumPairs.cbegin(); it != this->m_EnumPairs.cend(); it++)
{
if (it->first == enumValue)
throw std::exception("Enum key already defined");
if (it->second == this->m_NextValue)
throw std::exception("Enum value already exists");
}
this->m_EnumPairs[enumValue] = this->m_NextValue++;
}
void Add(T enumValue, DWORD value)
{
this->m_NextValue = value;
this->Add(valueName);
}
private:
std::unordered_map<T, DWORD> m_EnumPairs;
DWORD m_NextValue;
};
Derived Enums .h
namespace Test
{
typedef enum _Enum
{
EnumValue1,
EnumValue2,
EnumValue3,
EnumValue4,
EnumValue5,
EnumValue6,
EnumValue7,
EnumValue8
} Enum;
}
class DerivedEnum : public Enums<Test::Enum>
{
public:
DerivedEnum(DWORD versionCode);
~DerivedEnum();
};
Derived Enums .cpp
DerivedEnum::DerivedEnum(DWORD versionCode) : Enums(versionCode)
{
this->Add(Test::EnumValue1);
this->Add(Test::EnumValue2);
this->Add(Test::EnumValue3);
this->Add(Test::EnumValue4);
this->Add(Test::EnumValue5);
this->Add(Test::EnumValue6);
if (versionCode > 200)
this->Add(Blocks::EnumValue7);
this->Add(Blocks::EnumValue8);
}
DerivedEnum::~DerivedEnum()
{
}
Usage
Enums<DerivedEnum>::Count(250)
I'm know I'm doing something wrong, I just want to know what I'm doing wrong and how I can do something like Enums<DerivedEnum>::Count(250). The error centers around the static Count function and the error goes completely away when I remove the static function and the call to it.
EDIT
To answer a question asked in the comments: The usage of this would be to get the size or number of entries in the "enum". For example, if I needed to read a structure from a file and in that structure is an array of items:
struct DataFromFile
{
int flags;
int array[SIZE_OF_ENUM];
}
If I have different versions of this file that contain a different number of items in the array, I can use the "enum" class to determine how many bytes to read based on Enums<DerivedEnum>::Count(250). If that doesn't make sense, I'll try to clarify even further.
UPDATE
As ZDF said in the comments, changing unordered_map to map fixed the issue. unordered_map requires a hash function and the default does not know how to hash my custom class, map does not require a hash and therefore works perfect in this situation.

Storage of function pointer in polymorphic class without explicit template specialization

I am trying to create a helper class to abstract invoking function pointers. With feedback from others on SO, I am using a polymorphic class to achieve this (shown below). Templates are also used to reduce code duplication.
typedef void(*PFNFOO1) (int);
typedef void(*PFNFOO2) (double);
typedef void(*PFNBAR1) (long);
typedef void(*PFNBAR2) (float);
typedef struct FOO_TABLE
{
PFNFOO1 pfnFoo1;
PFNFOO2 pfnFoo2;
} FOO_TABLE;
typedef struct BAR_TABLE
{
PFNBAR1 pfnBar1;
PFNBAR2 pfnBar2;
} BAR_TABLE;
enum TABLE_TYPE
{
TYPE_FOO = 0,
TYPE_BAR = 1,
};
template <typename T>
class FooBarImpl : public FooBarBase
{
public:
// GetFunc is created to centralize needed validation before function is invoked
void* GetFunc(size_t funcOffset)
{
// do some validation
return reinterpret_cast<void*>(m_FooBarTable + funcOffset);
}
void* GetpfnFoo1() { return GetFunc(offsetof(T, pfnFoo1)); }
void* GetpfnFoo2() { return GetFunc(offsetof(T, pfnFoo2)); }
void* GetpfnBar1() { return GetFunc(offsetof(T, pfnBar1)); }
void* GetpfnBar2() { return GetFunc(offsetof(T, pfnBar2)); }
T* m_FooBarTable;
};
class FooBarBase
{
public:
static FooBarBase* CreateFooBar(TABLE_TYPE tableType)
{
switch(tableType)
{
case (TYPE_FOO) :
{
return new FooBarImpl<FOO_TABLE>();
}
break;
case (TYPE_BAR) :
{
return new FooBarImpl<BAR_TABLE>();
}
break;
}
}
virtual void* GetpfnFoo1() = 0;
virtual void* GetpfnFoo2() = 0;
virtual void* GetpfnBar1() = 0;
virtual void* GetpfnBar2() = 0;
};
int _tmain(int argc, _TCHAR* argv[])
{
{
FooBarBase *pFooBar = FooBarBase::CreateFooBar(TYPE_FOO);
// Initialize Foo table
auto p = reinterpret_cast<PFNFOO1>(pFooBar->GetpfnFoo1());
int parameter = 1;
p(parameter);
}
{
FooBarBase *pFooBar = FooBarBase::CreateFooBar(TYPE_FOO);
// Initialize Bar table
auto p = reinterpret_cast<PFNBAR2>(pFooBar->GetpfnBar2());
float parameter = 1.0f;
p(parameter);
}
return 0;
}
This is currently giving me complication errors as "C2039: 'pfnBar1' : is not a member of 'FOO_TABLE'" which makes sense because one of the implicit template specialization will try to do "offsetof(FOO_TABLE, pfnBar1)," which isn't allowed. I have two questions. First, I am wondering what's the best way to address this error. I think I can possibly address this by providing explicit template specializations for FooBarImpl and FooBarImpl, but I'd like to avoid doing that because it means that if I were to add a new table type later, I'd have to add another specialization. Also, it increases code duplication. Therefore, if there's a way to fix this issue without explicit template specialization, please let m know.
For my second question, if explicit template specialization cannot be avoided, I have also tried this:
class FooBarBase;
template <typename T>
class FooBarImpl : public FooBarBase
{
};
template <>
class FooBarImpl<FOO_TABLE> : public FooBarBase
{
public:
typedef FOO_TABLE T;
// GetFunc is created to centralize needed validation before function is invoked
void* GetFunc(size_t funcOffset)
{
// do some validation
return reinterpret_cast<void*>(m_FooBarTable + funcOffset);
}
void* GetpfnFoo1() { return GetFunc(offsetof(T, pfnFoo1)); }
void* GetpfnFoo2() { return GetFunc(offsetof(T, pfnFoo2)); }
T* m_FooBarTable;
};
template<>
class FooBarImpl<BAR_TABLE> : public FooBarBase
{
public:
typedef BAR_TABLE T;
// GetFunc is created to centralize needed validation before function is invoked
void* GetFunc(size_t funcOffset)
{
// do some validation
return reinterpret_cast<void*>(m_FooBarTable + funcOffset);
}
void* GetpfnBar1() { return GetFunc(offsetof(T, pfnBar1)); }
void* GetpfnBar2() { return GetFunc(offsetof(T, pfnBar2)); }
T* m_FooBarTable;
};
But for some reason, I keep getting this error "error C2504: 'FooBarBase' : base class undefined" even if it was working fine before I specialized the templates.
If anyone has ideas about these 2 questions, I'd really appreciate your feedback. Thanks.