Могу я это упростить?

typedef void (FunctionSet::* Function)();

class MyFunctionSet : public FunctionSet
{
protected:
    void addFunctions()
    {
        addFunction(Function(&MyFunctionSet::function1));
    }

    void function1()
    {
        // Do something.
    }
};

Метод addFunction добавляет функцию в список базового класса
. которые затем можно перечислить для вызова всех функций.

Есть ли способ упростить (меньше печатать) добавление функций?

Не могли бы вы изменить название? Когда вопросы звучат так банально, иногда просто кажется, что это ловушка, чтобы заставить людей прийти и посмотреть (и да, я был в ловушке!) Описательные названия вопросов гораздо более уместны. Что касается фактического ответа, я полагаю, мне нужно освежить свой C++;)

TJB 20.01.2009 02:53

Некоторый контекст был бы неплохим. Какую проблему вы пытаетесь решить? Где / как это будет использоваться?

jalf 20.01.2009 03:20

ну, в комментариях к одному ответу вы заявили, что «хотите, чтобы все было эффективно». Я думаю, нам нужно знать об этом больше, чтобы дать вам более точные ответы. Вы можете хранить объекты функций, но тогда вам также нужно сохранить указатель this.

Johannes Schaub - litb 20.01.2009 03:29
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
3
367
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Полагаю, вы могли бы перегрузить оператор сложения.

Можете ли вы объяснить, чего вы этим пытаетесь достичь?

Это похоже на довольно плохой дизайн, разве у вас не может быть абстрактный класс («интерфейс C++»), такой как «Computable», который имеет чистую виртуальную функцию1, подкласс Computable для каждой реализации, а затем MyFunctionSet поддерживает набор Computable?

Есть ли конкретная причина, по которой вы используете указатели на функции?

Функторы обычно эффективнее более, чем указатели на функции. Но это когда они реализуются без виртуальных функций. В любом случае «создание классов» - это ерунда, и это не требует дополнительных затрат. Для меня это пахнет преждевременной оптимизацией.

jalf 20.01.2009 03:16

Что именно вы делаете, чтобы эффективность была важнее хороших принципов объектно-ориентированного проектирования? Динамическое связывание так же эффективно, как и ручное управление указателями ваших собственных функций.

Uri 20.01.2009 03:21

Думаю, это вопрос стиля и самого проекта. Указатели на функции в C++ опасны. Если вас беспокоит экономия на вводе текста, используйте лучшую IDE или инструмент CASE.

Uri 20.01.2009 04:44

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

Uri 20.01.2009 04:44

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

Uri 20.01.2009 04:47
Ответ принят как подходящий

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

Чтобы ответить на ваш актуальный вопрос - я бы сделал addFunction шаблоном:

void addFunctions() {
    addFunction(&MyFunctionSet::function1);
}

Измените addFunction в базовом классе на это:

template<typename Derived>
void addFunction(void(Derived::*f)()) {
    myFunctions.push_back(static_cast<Function>(f));
}

Лучше использовать static_cast, потому что он скажет вам, действительно ли Derived не является производным от FunctionSet.

Вероятно, нет способов удалить части & или MyFunctionSet ::?

Tamara Wijsman 20.01.2009 04:04

нет, никак: / подождите, я имею в виду, если вы назначаете указатель без регистра - тогда это запрещено. звучит так, как будто вы это ошиблись, я не хотел говорить, что ваш код запрещен C++. извините, если это звучит немного непонятно :)

Johannes Schaub - litb 20.01.2009 04:10

Итак, я склонен в некоторой степени не согласиться с Ури.

Если вы реализуете паттерн Observer, вам нужно иметь возможность сказать:

«Когда объект X выполняет Y, я хочу выполнить код Z».

Использование подхода, основанного исключительно на абстрактном классе, - не лучший способ сделать это. Требовать отдельный класс (особенно в C++) для каждого обработчика событий - это излишне. Если вы пришли с Java, они все так и делают. Однако в Java есть 2 функции, которые делают это лишь слегка раздражающим: анонимный класс и классы-члены «экземпляра». Это позволяет вам определять несколько обработчиков для нескольких событий в одном классе. Вы должны добавить к методам префикс "class {", но вы можете это сделать.

В C++ нет ни анонимных классов, ни классов-членов-«экземпляров». Это делает использование абстрактного класса для событий гораздо более громоздким, если у вас есть несколько обработчиков, которым необходимо совместно использовать состояние. Вы должны выполнить аналог создания замыкания вручную, что довольно быстро может раздражать.

.NET использует делегаты для обработки событий, которые в основном представляют собой указатели на функции с типобезопасностью. Они делают код для обработки событий очень простым. Однако, в отличие от указателей функций в C++, делегат объединяет указатели функций-членов и указатели статических функций. По сути, он может каррировать параметр «this» любой функции-члена, оставляя указатели на функции, которые выглядят так же, как указатель статической функции. Это означает, что тип объекта, обрабатывающего событие, не является частью «интерфейса» события. Это делает его очень гибким.

Вы не можете сделать это напрямую, используя указатели функций-членов C++, потому что тип «this» в конечном итоге становится частью типа указателя функции, тем самым ограничивая ваши обработчики только появлением в текущем классе.

Лучшее решение для C++ - это их гибрид. Вам нужен такой общий интерфейс:

class EventHandler
{
public:
    virtual void Handle() = 0;
};

А затем реализация для таких функций-членов

template <class T>
class MemberFuncEventHandler : public EventHandler
{
public:


    MemberFuncEventHandler(T * pThis, void (T::*pFunc)()) : m_pThis(pThis), m_pFunc(pFunc)
    {
    }

    void Handle()
    {
        (m_pThis->*m_pFunc)();
    } 
private:
    T* m_pThis;
    void (T::*m_pFunc)();
};

template <class T>
EventHandler * Handler(T * pThis, void (T::*pFunc)())
{
    return new MemberFuncEventHandler<T>(pThis, pFunc);
}

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

Handlers += Handler(obj, &(c1::foo));
Handlers += Handler(obj, &(c2::bar));
Handlers += Handler(blaa); // for static methods...

Создание отдельного класса не является излишним с точки зрения дизайна, только с точки зрения набора текста (в конце концов, Java и C# создают классы, когда вы используете анонимность). Я считаю, что инструмент CASE или хорошая IDE могут сэкономить эту работу, сохранив при этом хороший дизайн.

Uri 20.01.2009 04:46

Хотя мне очень нравится ваш код, я сомневаюсь, что средний (не зависимый от SO) программист на C++ сможет правильно его понять и поддерживать и, следовательно, с осторожностью будет использовать его в производстве.

Uri 20.01.2009 04:46

Не уверен, что код такой сложный ... Написание заняло у меня около 5 минут. Большую часть времени нужно было просто уточнить синтаксис указателя на функцию-член. Я думаю, что есть реализации в boost (например, кто-то из упомянутых ниже), которые можно просто загрузить, и, вероятно, скоро они будут в стандарте.

Scott Wisniewski 20.01.2009 08:17

Uri, по поводу инструмента CASE ... Свое мнение по этому поводу я написал в своем блоге. Вы можете прочитать это на specbug.com/blog/2009/1/21/scotts-law-of-software-2.html

Scott Wisniewski 22.01.2009 00:06

Если вы пытаетесь создать систему обратного вызова или что-то еще, я бы порекомендовал библиотеку Boost.Signals. По сути, он объединяет функции и вызывает группу функций по команде (как и любая другая библиотека сигналов), но он также предназначен для работы с Boost.Bind и Boost.Function, которые великолепны.

Пример:

using boost::function
using boost::bind
using boost::signal

void some_other_func();    

Foo a;
Bar b;

signal<void()> sig;

sig.connect(bind(&Foo::foo,&a));
sig.connect(bind(&Bar::bar,&b));
sig.connect(some_other_func);

sig(); // calls -> a.foo(), b.bar(), some_other_func()

Также поддерживает блокировку, сложное управление подключением и другие полезные вещи.

Я привел более сложный пример. Если вы посмотрите документацию Boost.Signals, вы увидите, что это действительно приятный минималистичный интерфейс. Для типобезопасных универсальных обратных вызовов я не видел лучшей библиотеки.

user21714 20.01.2009 06:45

Другие вопросы по теме