Использование шаблона вместо переключателя - PullRequest
8 голосов
/ 13 апреля 2011

Я хочу выполнить в моем коде набор тестов, которые похожи, но меняются только в зависимости от параметра.

Я мог бы написать это с помощью оператора switch:

bool doTest(EnumSensorFamily family, const StructSensorProposal& proposed)
{
  switch (family)
  {
  case FAM1:
    return (ExpectedFam1 == proposed.Fam1SensorId);
    break;
  case FAM2:
    return (ExpectedFam2 == proposed.Fam2SensorId);
    break;
  case FAM3:
    return (ExpectedFam3 == proposed.Fam3SensorId);
    break;
  default:
    ERROR ("Unexpected family");
    return false;
  }
}

Я думал об этом с помощью специализаций шаблонов

template <EnumSensorFamily family>
bool doTest(const StructSensorProposal& proposed);

template<>
bool doTest<FAM1> (const StructSensorProposal& proposed)
{
  return (ExpectedFam1 == proposed.Fam1SensorId);
}

template<>
bool doTest<FAM2> (const StructSensorProposal& proposed)
{
  return (ExpectedFam2 == proposed.Fam2SensorId);
}

template<>
bool doTest<FAM3> (const StructSensorProposal& proposed)
{
  return (ExpectedFam3 == proposed.Fam3SensorId);
}

Есть ли какая-то польза от этого, кроме как избегать оператора switch, содержащего почти идентичные случаи?

В идеале я хотел бы иметь возможность написать один метод для сокращения затрат на обслуживание.

спасибо

Ответы [ 5 ]

8 голосов
/ 13 апреля 2011

Построение ответа Эндрю ...

Обратите внимание, что EnumSensorFamily family должен быть известен во время компиляции.Если он неизвестен до времени выполнения, вам нужно будет написать switch, чтобы выбрать шаблон, и вы вернетесь к тому, с чего начали.

Другой способ сделать это - с помощью шаблона Traits:

template <EnumSensorFamily family>
struct SensorTraits;

template <>
struct SensorTraits<FAM1>
{
    const EnumSensorFamily kFamilyID = ExpectedFam1;
};

template <>
struct SensorTraits<FAM2>
{
    const EnumSensorFamily kFamilyID = ExpectedFam2;
};

template <>
struct SensorTraits<FAM3>
{
    const EnumSensorFamily kFamilyID = ExpectedFam3;
};

template <EnumSensorFamily family>
bool doTest(const StructSensorProposal& proposed)
{
  return (SensorTraits<family>::kFamilyID == proposed.Fam1SensorId);
}

Если вы попытаетесь использовать doTest с семейством датчиков, в котором отсутствует специализация по признакам, вы получите ошибку компиляции.Также обратите внимание, что вы никогда не создаете экземпляр объекта признаков, вы просто используете его определения.

Это позволяет вам повторно использовать константы, typedefs, что угодно в нескольких функциях.Кроме того, добавление нового семейства не включает в себя прочесывание всего кода в поисках каждого оператора switch, который заботится.Все, что вам нужно сделать, это создать новую специализацию SensorTraits.

РЕДАКТИРОВАТЬ: Вы можете сделать поле зависимым от семейства датчиков с помощью указателя на элемент :

template <>
struct SensorTraits<FAM1>
{
    const EnumSensorFamily kFamilyID = ExpectedFam1;
    int StructSensorProposal::*proposalField = &StructSensorProposal::fam1field;
};

// ...

template <EnumSensorFamily family>
int getProposedField(const StructSensorProposal& proposed)
{
    return proposed.*SensorTraits<family>::proposalField;
}

Вы также можете ввести, скажем, typedef для типа данных датчика:

template <>
struct SensorTraits<FAM1>
{
    const EnumSensorFamily kFamilyID = ExpectedFam1;
    typedef uint16_t data_type;
    data_type StructSensorProposal::*proposalField = &StructSensorProposal::fam1field;
};

// ...

template <EnumSensorFamily family>
SensorTraits<family>::data_type getProposedField(const StructSensorProposal& proposed)
{
    return proposed.*SensorTraits<family>::proposalField;
}

Я не проверял их;вам может понадобиться const или static там.

6 голосов
/ 13 апреля 2011

Преимущество - очень небольшое потенциальное улучшение производительности, если компилятору не удается правильно оптимизировать switch (т. Е. Если он не генерирует тот же код, что и для шаблонного решения, что выполнимо для современного встроенного компилятора) , Конечно, только если family является константой времени компиляции - в противном случае шаблоны неприменимы, и лучшая оптимизация, которую компилятор может найти для switch, - это вычисленный переход или таблица переходов.

Если ваш код всегда выглядит как return (ExpectedFamN == proposed.FamNSensorId);, я бы предпочел использовать массивы для ожидаемых значений и идентификаторов датчиков и индексировать их на основе family.

2 голосов
/ 13 апреля 2011

Невозможно использовать шаблоны в следующем случае:

const  EnumSensorFamily familyCompileTime = FAM3; // Compile time constant
EnumSensorFamily family = GetFimilyInRunTime(); // Run time variable
doTest1(family, proposed); // ok
doTest2<family>(proposed); // error;
doTest2<familyCompileTime >(proposed); // OK;
1 голос
/ 13 апреля 2011

Ну, в некотором смысле ... вы перемещаете обработку от времени выполнения к времени компиляции. Компилятор создаст функции и будет использовать их соответствующим образом, вместо того, чтобы во время выполнения проходить оператор switch.

Кроме того, это приведет к более чистому коду.

0 голосов
/ 15 февраля 2014

Существует также преимущество размера двоичного файла, только исполняемые шаблоны будут в исполняемом файле. Если у вас огромный переключатель, это может быть важно для размера exe! ​​

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...