Nano Hash - криптовалюты, майнинг, программирование

Метапрограммирование C/C++ с использованием препроцессора

Итак, у меня есть это огромное дерево, которое в основном представляет собой большой переключатель/кейс со строковыми ключами и различными вызовами функций для одного общего объекта в зависимости от ключа и одной части метаданных.

Каждая запись в основном выглядит так

} else if ( strcmp(key, "key_string") == 0) {
    ((class_name*)object)->do_something();
} else if ( ...

где do_something может иметь разные вызовы, поэтому я не могу просто использовать указатели на функции. Кроме того, некоторые ключи требуют приведения объекта к подклассу.

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

Мне пришло в голову, что я могу использовать макросы, чтобы упростить это до чего-то вроде

case_call("key_string", class_name, do_something());
case_call( /* ... */ )

где case_call будет макросом, который расширит этот код до первого фрагмента кода.

Тем не менее, я очень сомневаюсь, будет ли это считаться хорошим стилем. Я имею в виду, что это уменьшило бы работу по набору текста и улучшило бы DRYness кода, но тогда это действительно несколько злоупотребляет системой макросов.

Вы бы пошли по этому пути или, скорее, напечатали бы все это? И каковы были бы ваши доводы для этого?

Изменить

Некоторое уточнение:

Этот код используется в качестве связующего слоя между упрощенным API сценариев, который обращается к нескольким различным аспектам API C++ в виде простых свойств "ключ-значение". Однако в C++ свойства реализованы по-разному: у некоторых есть методы получения/установки, некоторые задаются в специальной структуре. Действия сценариев ссылаются на объекты C++, приведенные к общему базовому классу. Однако некоторые действия доступны только для определенных подклассов и должны быть отменены.

В дальнейшем я могу изменить фактический C++ API, но на данный момент его следует считать неизменным. Кроме того, это должно работать на встроенном компиляторе, поэтому boost или C++ 11 (к сожалению) недоступны.


  • Почему голоса закрыть - это очень хороший, правильный вопрос. Что мне здесь не хватает? 17.05.2012
  • Это хороший вопрос, но он не подходит для нашего формата вопросов и ответов. Мы ожидаем, что ответы, как правило, будут включать факты, ссылки или конкретный опыт; этот вопрос, скорее всего, потребует мнения, дебатов, аргументов, опроса или расширенного обсуждения. 17.05.2012
  • Это уже +5 баллов, так что это как бы говорит о том, что другие, в том числе и я, заинтересованы в результате. Ответ ниже уже довольно хорош. 17.05.2012
  • В C++ 11 вы можете использовать словарь лямбда-выражений, чтобы упростить это. 17.05.2012
  • @ Адам, как и лямбда-выражения, я предполагаю, что tr1 :: function тоже будет работать 17.05.2012
  • Я бы не стал стесняться макросов, также я думаю, что boost может иметь то, что вам нужно, в библиотеке препроцессора, но я еще не использовал это 17.05.2012
  • Можете ли вы показать более конкретный пример того, что у вас есть прямо сейчас? 17.05.2012
  • Те, кто проголосовал за закрытие: можете ли вы придумать лучший форум для этого вопроса? Я не могу! 17.05.2012

Ответы:


1

Я предлагаю вам немного поменяться ролями. Вы говорите, что объект уже является некоторым классом, который знает, как обращаться с определенной ситуацией, поэтому добавьте virtual void handle(const char * key) в свой базовый класс и позвольте объекту проверить в реализации, применим ли он к нему, и сделать все необходимое.

Это не только устранило бы длинную цепочку if-else-if, но также сделало бы более безопасным тип и дало бы вам больше гибкости в обработке этих событий.

17.05.2012
  • Это не устраняет длинную цепочку if-else-if, а просто перемещает/переставляет ее. Тем не менее, это все еще очень хорошая идея. 17.05.2012
  • @MooingDuck Это не устранит отдельные «если», но устранит центральный длинный список случаев, которые необходимо обработать. Каждая проверка будет находиться в классе, где происходит фактическая обработка, поэтому места, которые необходимо изменить для добавления новых функций, будут расположены близко друг к другу. 17.05.2012
  • Это интересная идея. Это потребовало бы от меня изменения классов реализации, что по ряду причин сейчас нежелательно, но это, безусловно, хорошая идея. 17.05.2012
  • На самом деле я мог бы реализовать дерево решений в виде независимой функции с несколькими специализациями в зависимости от типа подкласса. интересные мысли... 17.05.2012

  • 2

    Мне кажется, это уместное использование макросов. В конце концов, они созданы для исключения синтаксического повторения. Однако, когда у вас есть синтаксическое повторение, это не всегда вина языка — вероятно, есть лучшие варианты дизайна, которые позволят вам вообще избежать этого решения.

    Общая мудрость заключается в использовании таблицы сопоставления ключей с действиями:

    std::map<std::string, void(Class::*)()> table;
    

    Затем найдите и вызовите действие за один раз:

    object->*table[key]();
    

    Или используйте find для проверки отказа:

    const auto i = table.find(key);
    if (i != table.end())
        object->*(i->second)();
    else
        throw std::runtime_error(...);
    

    Но если, как вы говорите, для функций нет общей подписи (т. е. вы не можете использовать указатели функций-членов), то то, что вы на самом деле должны делать, зависит от особенностей вашего проекта, чего я не знаю. т знаю. Возможно, макрос — единственный способ избежать повторения, которое вы видите, или, возможно, есть лучший способ сделать это.

    Спросите себя: почему мои функции принимают разные аргументы? Почему я использую приведение типов? Если вы работаете с типом объекта, скорее всего, вам нужно ввести общий интерфейс.

    17.05.2012
  • где do_something может иметь разные вызовы, поэтому я не могу просто использовать указатели на функции. ‹- похоже, что не все функции будут void(Class::*)(). Некоторым могут потребоваться аргументы. По крайней мере, это то, что я понял. 17.05.2012
  • На самом деле у меня есть два разных вида вызовов, так что это действительно сработало бы, если бы я использовал две карты. 17.05.2012
  • Новые материалы

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

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..