2017-01-23 2 views
0

Я пишу несколько библиотек arduino и хочу улучшить удобочитаемость/добавить некоторый синтаксический advar.Целенаправленное создание анонимного объекта на куче

То, что я хотел бы сделать, это создать объекты в куче так, что будет выглядеть так:

Panel panel( 
    Button(1).on(Click(clickfunc)), 
    Button(2).on(Hold(holdfunc, 1000)) 
); 

(кнопка, нажмите, удерживайте все классы и внутренне управляемые с помощью связанных списков (так что они Арен 't constant.))

Я пробовал писать это так, но я наткнулся на проблемы со ссылками на временные.

В настоящее время я могу использовать:

Button button1(1), button2(2); 
Click theClick(clickFunction); 
Hold theHold(holdFunction, 1000); 
Panel(button1.on(theClick), button2.on(theHold)); 

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

Некоторые тяжелые укороченные выдержки из таких классов, как они сейчас.

class Button { 
    Handler *_first; 
    Button(int no){...} 
    Button & on(Handler &handler){ 
     handler._next = _first; 
     _first = &handler; 
     return *this; 
    } 
    void handle(int oldValue, int newValue) { 
     Handler *handler; 
     for(handler = _first; handler; handler = handler->_next){ 
      handler->handle(oldValue, newValue); 
     } 
    } 
} 
class Handler { 
    Handler *_next; 
    virtual void handle(int oldValue, int newValue) = 0; 
    ... 
} 
class Click : public Handler { 
    ... 
} 
class Hold : public Handler { 
    ... 
} 

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

+1

Примечание: Объекты в куче всегда анонимны, так как они никогда не имеют name: они никогда не являются переменными. –

+0

Можете ли вы добавить точные ошибки, которые вы получали, когда пытались выполнить одну строку? – George

+0

@drescherjm: может быть. Но почему вы думаете, что это не на куче? (Или я неправильно использую слово «куча» в контексте cpp?)) – Scheintod

ответ

1

Если у вас возникли проблемы с оборванными ссылками с приведенным выше кодом, я подозреваю, что вы создаете связанный список, который создает ссылки (или указатель), которые указывают на тот элемент в стеке.

Подозреваю также, что ваша подпись выглядит следующим образом:

Button& on(const Event& event) { /* ... */ } 

Чтобы помочь вам с вашей проблемой, я предлагаю, чтобы изменить подпись вашей on функции на что-то вроде этого:

template<typename EventType> 
Button& on(EventType&& event) { 

} 

Таким образом, вы можете направить объект в кучу и использовать некоторую форму типа easure, чтобы поместить его в свой список:

struct Handler { 
    virtual void handle(int oldValue, int newValue) = 0; 

    // Defaulted virtual destructor 
    virtual ~Handler() = default; 
}; 

template<typename T> 
struct HandlerImpl : Handler { 
    // constructors 
    HandlerImpl(T h) : handler{std::forward<T>(h)} {} 

    void handle(int oldValue, int newValue) { 
     handler.handle(oldValue, newValue); 
    } 

    // We use the compiler generated destructor 

private: 
    remove_rvalue_reference_t<T> handler; 
}; 

template<typename HandlerType> 
Button& on(HandlerType&& event) { 
    // See the code example below 
} 

Какие изменения в остальной части вашего кода?

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

Кроме того, Click и Hold не нужно расширять любой класс и не нуждаться в виртуальной функции или виртуальных деструкторах.

Если вы не хотите, второй синтаксис для хранения ссылок и использовать копию вместо того, чтобы заменить remove_rvalue_reference_t на std::remove_reference_t.

Этот шаблон, который я показал вам могут быть применены для Button, и для любого типа виджета вы хотите.


Вот как remove_rvalue_reference_t реализуется:

template<typename T> struct remove_rvalue_reference { using type = T; }; 
template<typename T> struct remove_rvalue_reference<T&&> { using type = T; }; 
template<typename T> using remove_rvalue_reference_t = typename remove_rvalue_reference<T>::type; 

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

Во-первых, список понравился медленно, а список ручных списков хуже. Я настоятельно рекомендую вам использовать std::vector. Во-вторых, std::unique_ptr является предпочтительным способом удержания указателей владения. Так, только следуя этим и шаги, упомянутые выше, ваш код должен выглядеть следующим образом:

struct Button { 
    std::vector<std::unique_ptr<Handler>> _handlers; 

    Button(int no) { /* ... */ } 

    // This function will work for any type that 
    // happen to have an `handle` function. 
    template<typename H> // <--- H is the handler type 
    Button& on(H&& handler) { // H&& in this case means forwarding reference. 
     // We add (emplace) a new HandlerImpl, allocated on the heap using `std::make_unique` 
     _handlers.emplace_back(
      std::make_unique<HandlerImpl<H>>(std::forward<H>(handler)) 
     ); 

     return *this; 
    } 

    void handle(int oldValue, int newValue) { 
     // We use a range for loop here to iterate on the vector 
     for (auto&& handler : _handlers) { 
      handler->handle(oldValue, newValue); 
     } 
    } 
}; 

// We do not extends anything 
struct Click { 
    // Notice that the function is not virtual 
    void handle(int oldVal, int newVal) {/* ... */} 
}; 

struct Hold { 
    void handle(int oldVal, int newVal) {/* ... */} 
}; 

Вот живой пример на Coliru

+0

Спасибо за ответ! Но святое дерьмо (!) Теперь мне сначала нужно глубоко вникать во все виды C++, о которых я не знаю. (Но стыдно за меня, я попросил волшебство;) – Scheintod

+0

@Scheintod Я добавил более конкретный пример, который работает с вашим кодом. –

+0

Еще раз спасибо. Все еще пытаюсь понять, что происходит :) Я думаю, что на arduino у меня нет std :: vector. Гектометр std :: unique_ptr ему тоже не нравится ... – Scheintod

Смежные вопросы