Обеспечить содержание функции PV при создании объекта в C ++ - PullRequest
0 голосов
/ 11 мая 2018

В Java вы можете создать объект, одновременно предоставляя (или перегружая) абстрактные функции внутри объекта, таким образом:

ActionListener al = new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        // Whatever in here
    }
};

Мне действительно нравится такой способ, и мне было интересно, есть ли в C ++ похожая конструкция.

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

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

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

Так что дочерние классы - единственный путь, или есть какая-то менее известная конструкция в каком-то более новом стандарте C ++, о котором я не знаю, который мог бы делать то, что я хочу?


Чтобы немного расширить - идея в том, чтобы иметь такой класс:

class Thread {
    // other stuff
    public:
        virtual void setup() = 0;
        virtual void loop() = 0;
        // other functions, some virtual but not pure
};

Thread threadOne {
    void setup() {
        // Init code for this thread
    }
    void loop() {
        // Run code for this thread
    }
};

Thread threadTwo {
    void setup() {
        // Init code for this thread
    }
    void loop() {
        // Run code for this thread
    }
};

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

Он предназначен для работы во встроенной системе с упрощенной реализацией C ++ (это g ++, но без полной STL). Конечные пользователи - не самая лучшая группа, поэтому ее нужно понимать как можно проще.


Анонимные дочерние классы ближе всего к тому, что я хотел бы (хотя все еще не идеален). Я могу использовать макросы CPP, чтобы помочь абстрагировать некоторые синтаксические сахара реализации класса, которые могли бы помочь.


Вот компилируемая конструкция, которую я придумал. Есть ли что-то «не так» в этом подходе с учетом указанных выше ограничений?

#define THREAD(NAME, CONTENT) class : public Thread {\
    public:\
CONTENT\
} NAME;


class Thread {
    private:
        uint32_t stack[256]; // 1kB stack
        volatile bool _running;

    public:
        virtual void setup() = 0;
        virtual void loop() = 0;

        void start();
        void stop();
        uint8_t state();

        static void spawn(Thread *thr);
        void threadRunner();
};

void Thread::spawn(Thread *thread) {
    thread->threadRunner();
}

void Thread::start() {
    Thread::spawn(this);
}

void Thread::threadRunner() {
    _running = true;
    setup();
    while (_running) {
        loop();
    }
}

void Thread::stop() {
    _running = false;
}

uint8_t Thread::state() {
    return 0;
}

THREAD(myThread,
    void setup() override {

    }

    void loop() override {

    }
)

void setup() {
    myThread.start();
}

void loop() {
}

Очевидно, что на самом деле он еще ничего не делает - весь внутренний поток является отдельной проблемой и будет перенесен из некоторого существующего кода, который я написал несколько лет назад. В основном меня интересует упрощение интерфейса для конечного пользователя.

Ответы [ 2 ]

0 голосов
/ 11 мая 2018

хорошо, как вы уже упоминали, одним из способов были бы дочерние классы.

другим способом было бы предоставление некоторых std :: functions (или lambdas), либо в конструкторе, либо с некоторыми функциями set.

сохраните функцию как член и вызовите ее при вызове вашей «виртуальной» функции-члена: Если вы хотите, чтобы она была необязательной:

class MyBase
{
public:
   MyBase();
   void SetFunc(const std::function<int()>& myFun)
   {
      m_myFun = myFun;
   }

   int MyVirtFunc()
   {
      if(m_myFun)
      {
         return m_myFun();
      }
      else
      {
         return 42;
      }
   }   
private:
   std::function<int()> m_myFun;
}

, если вы хотите, чтобы эти функции были обязательными, поместите их вконструктор:

class MyBase
{
public:
   MyBase(const std::function<int()>& myFun)
       : m_myFun(myFun) {}

   int MyVirtFun()   {  return m_myFun(); }
private:
   const std::function<int()> m_myFun;
}
0 голосов
/ 11 мая 2018

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

class ActionListener
{
    std::function<void(int)> _action_performed;
public:
    template<class CB>
    ActionListener(CB cb) : _action_performed(cb) {}

    void click() { _action_performed(0); }
};

int main()
{
    ActionListener al([](int n) { std::cout << "Action Performed #" << n << "\n"; });
    al.click(); // prints "Action Performed #0"
}

живое демо


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

Что, говоря семантически, означает предоставление поведения по умолчанию. Это возможно:

ActionListener(CB cb) : _action_performed(cb) {} // construct an AL with the given callback

ActionListener() : _action_performed(default_action_performed) {} // construct an AL with a default callback
void default_action_performed(int n) { /*...*/ }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...