Я работаю над кодом, который содержит несколько функций со следующим шаблоном
memberType* var = NULL;
switch(someVariable)
{
case 0: var = &object.MemberVariable1; break;
case 1: var = &object.MemberVariable2; break;
case 2: var = &object.MemberVariable3; break;
}
Переменные-члены одного типа. В некоторых коммутаторах есть несколько десятков случаев, которые загромождают тело функции тем, что в основном представляют собой данные. Я хотел бы создать массив, карту или что-то подобное, чтобы я мог получить доступ к членам на основе входного значения.
Я думаю о том, чтобы иметь что-то вроде
sometype array[size] = {member1, member2, member3}
чтобы функция могла содержать
memberType* var = array[index];
вместо выключателя.
1.
Моей первой мыслью было создание объединения в классе, которое содержит отдельные переменные-члены и массив, поэтому я могу получить доступ либо к отдельным членам, либо получить к ним доступ через массив + индекс. Это некрасиво, на первый взгляд неочевидно из кода и вынуждает члены-члены объявляться последовательно. Это также не позволяет мне получить доступ к одной и той же переменной-члену для разных индексов.
2.
Наличие массива, содержащего указатели на функции, которые возвращают отдельные переменные-члены, вынуждает меня создавать тонны однорядных функций получения.
3.
Имея статически размещенный объект, чтобы я мог сделать что-то вроде
int offsets[size] = {
*(int*)&staticObject.member1 - *(int*)&staticObject,
*(int*)&staticObject.member2 - *(int*)&staticObject,
*(int*)&staticObject.member3 - *(int*)&staticObject}
и использовать это
var = (memberType*)(*(int*)&object + offsets[index]);
это ужасно
Есть ли хороший способ избавиться от этого ненужного многословного шаблона?
Отказ от ответственности: я не тестировал код, используемый в примерах. Это просто для иллюстрации.
Редактировать: Я забыл упомянуть одну важную вещь: я не хочу менять размер класса из-за сериализации — мне еще предстоит понять реализацию, с которой я работаю.
Вы можете проверить указатели на участников. Это позволит вам реализовать ваше предложение (3) более четко.
Допустим, ваш класс выглядит следующим образом.
class X {
public:
memberType MemberVariable1;
memberType MemberVariable2;
memberType MemberVariable3;
};
Теперь мы определим массив указателей на переменные-члены.
typedef memberType X::*MemberVar;
MemberVar ptrs[3] = {
&X::MemberVariable1,
&X::MemberVariable2,
&X::MemberVariable3
};
Тогда вы можете использовать массив, как это.
X obj;
memberType var = obj.*(ptrs[index]);
Просто инкапсулировать switch
:
GetMemberVariableAt(int)
и переместите переключатель туда.object.GetMemberVariableAt(someVariable)
,Если вы сделаете это, не так уж важно, чтобы вы использовали неприятный переключатель для перехода к правильной переменной-члену, потому что этот переключатель будет внутренним для реализации вашего объекта, а не размазанным по всему коду. И у вас есть часть разума, что вы можете улучшить ее позже с минимальными усилиями, переключившись на массив или что-то еще, что пожелаете (или, возможно, ваши требования к производительности).
Если вы не управляете исходным кодом класса вашего объекта, вы все равно можете проксировать его или поставить перед ним адаптер, а также код для прокси или адаптера.
Решение, которое я здесь предлагаю, призвано упростить выбор между альтернативами, которые вы уже предлагаете в своем вопросе, за счет снижения важности фактора уродства.
Я должен добавить, что между switch
и массив решений ссылок на членов, я предпочитаю switch
потому что дублирование данных может быть богатым источником скрытых ошибок и сложного кода. Если ты можешь замещать переменные-члены с массивом внутри, я бы пошел на это через switch
, но если вам нужно поддерживать как массив, так и поля снаружи GetMemberVariableAt
Это дублирование данных, против которого я бы настоятельно рекомендовал.