Я пытаюсь обновить какой-то устаревший код C, который использует массив в качестве контейнера данных с именованным доступом через макросы, к более элегантному решению C ++ 17 (будет обновлено до C ++ 20, когда будет доступно, возможно) Решения C ++ 20 приветствуются). Извините, если кода много, но это мой первый вопрос StackOverflow. Приветствуются предложения по макету.
Текущее устаревшее оформление C:
#define WORD_ARR_SIZE 100
int16_t word_arr[WORD_ARR_SIZE]; //two byte variables
#define var0 word_arr[0] //macros are used to hide the array and use its members like variables
#define myValue word_arr[1]
#define myParameter word_arr[2]
#define free ((uint16_t)word_arr[3]) //'hidden' explicit cast needed when using the array as all data must be of the same type
#define var1 word_arr[4]
#define var2 word_arr[5]
#define var3 word_arr[6]
#define var4 word_arr[7]
#define var5 word_arr[7] //very easy to write the wrong index when adding new 'variables'
extern int send(int16_t* arr, size_t size); //The array is necessary as it needs to be fed to a library (code cannot be modified)
int main()
{
(uint16_t)var1 = UINT16_MAX; //'visible' explicit cast needed when using the array as all data is of the same type
myValues = 50;
for(int a = 20; a < 30; a++)
{
word_arr[a] = 10; //array is also used like it should be
}
return send(word_arr, WORD_ARR_SIZE);
}
Моя первая попытка решения проблема заключалась в использовании структуры вместо массива, это устраняло явное приведение и необходимость в макросах, но имело недостаток в том, что отсутствовал простой доступ через индекс, который был у реализации массива, заменяя его на уродливый reinterpret_cast.
//no need for pragma pack, the code doesn't care about padding
struct word_arr_t
{
int16_t var0; //no more macros
int16_t myValue;
int16_t myParameter;
uint16_t free; //no need for cast, value is alredy declared using the correct type
int16_t var1;
int16_t var2; //no way to get the index as if it was an array by simply using the value.
int16_t var3;
int16_t var4;
int16_t var5;
}word_arr;
constexpr size_t WORD_ARR_SIZE = sizeof(word_arr_t) / sizeof(uint16_t);
auto word_arr_p = reinterpret_cast<int16_t*>(&word_arr); //needed for indexed access
extern int send(int16_t* arr, size_t size);
int main()
{
word_arr.var1 = UINT16_MAX;
word_arr.myValues = 50;
for(int a = 20; a < 30; a++)
{
word_arr_p[a] = 10; //'hidden' pointer arithmetic to access the struct like an array
}
return send(word_arr_p, sizeof(word_arr_t));
}
Текущее решение: я создал настраиваемый шаблонный класс под названием SmartStruct, я передаю тип структуры и тип значений в шаблоне; Я создал перегрузку для оператора [], разрешив доступ через индекс, скрывающий уродливое reinterpret_cast;
/**
* \brief A wrapper for structs made of object of the same type, allows indexed access
* \tparam StructT struct type
* \tparam DataT struct data type
*/
template <typename StructT, typename DataT>
class SmartStruct
{
DataT* m_dataPointer;
public:
/**
* \brief let the struct be accessible from the outside as well
*/
StructT Data;
const size_t Count;
/**
* \brief Default constructor
*/
SmartStruct();
/**
* \brief Construct by struct copy
* \param data struct to copy
*/
explicit SmartStruct(const StructT& data);
/**
* \brief operator to access struct in array style
* \param index element to access
* \return element, if index >= size then first element
*/
DataT& operator[](size_t index);
};
template <typename StructT, typename DataT>
SmartStruct<StructT, DataT>::SmartStruct() : Data{}, Count{ sizeof Data / sizeof(DataT) }
{
m_dataPointer = reinterpret_cast<DataT*>(&Data);
}
template <typename StructT, typename DataT>
SmartStruct<StructT, DataT>::SmartStruct(const StructT& data) : Count{ sizeof data / sizeof(DataT) }
{
//copy the struct
Data = data;
m_dataPointer = reinterpret_cast<DataT*>(&Data);
}
template <typename StructT, typename DataT>
DataT& SmartStruct<StructT, DataT>::operator[](size_t index)
{
if (index >= Count)
{
return *m_dataPointer;
}
return m_dataPointer[index];
}
Пример использования:
struct word_arr_t
{
int16_t var0;
int16_t myValue;
int16_t myParameter;
uint16_t free;
int16_t var1;
int16_t var2;
int16_t var3; //Still no way to get array index from variable name
int16_t var4;
int16_t var5;
};
SmartStruct<word_arr_t, word> smart_word_arr{}; //Would love it if I could use std containers interface without having to implement it all by hand...
extern int send(int16_t* arr, size_t size);
int main()
{
word_arr_t& word_arr = smart_word_arr.Data;
word_arr.var1 = UINT16_MAX;
word_arr.myValues = 50;
for(int a = 20; a < 30; a++)
{
smart_word_arr[a] = 10;
}
return send(&smart_word_arr[0], smart_word_arr.Count);
}
Теперь, когда я получил контекст из того, что могу наконец, go к реальному вопросу:
Можно ли использовать std :: array в качестве контейнера данных для структуры? означает инициализацию его через структуру; это позволило бы получить доступ к данным через переменную, используя саму структуру, и через индекс, используя std :: array с дополнительным бонусом интерфейса std, без необходимости повторной реализации.
Моя текущая попытка получить это решение для работы:
struct word_arr_t
{
int16_t var0;
int16_t myValue;
int16_t myParameter;
uint16_t free;
int16_t var1;
int16_t var2;
int16_t var3; //Still no way to get array index from variable name
int16_t var4;
int16_t var5;
}word_struct;
std:.array<int16_t, sizeof(word_arr_t) / sizeof(word)> word_array{};
//std:.array<int16_t, sizeof(word_arr_t) / sizeof(word)> word_array{&word_struct}; would be lovely if I could do this.
//word_array.Data = reinterpret_cast<int16_t*>(&word_struct); this would also be good.
extern int send(int16_t* arr, size_t size);
int main()
{
word_struct.var1 = UINT16_MAX;
word_struct.myValues = 50;
//copy struct into array, very very bad as it's not usable unless you know when
//code writes to the struct and when code writes to the array,
//this could be solved by wrapping the array into a read only object but still not ideal
//and extremely slow especially if the struct is very large
memcpy(word_array.Data, &word_struct, sizeof(word_struct));
for(auto& a : word_array)
{
a = 10;
}
return send(word_array.Data, word_array.Size);
}