Exposing parameter types in a perfectly-forwarding function avoiding code repetition

  • A+
Category:Languages

I have an annoying scenario where I need to defer the initialization of some object state and allow the user to construct one on demand. E.g.

// user code  context c; // ...do something... c.initialize_state(a, b, c); 

// library code  class context { private:     class state     {         state(A a, B b, C c);          state(const state&) = delete;         state(state&&) = delete;     };      std::optional<state> _state; // or `boost::optional`  public:     template <typename... Xs>     void initialize_state(Xs&&... xs)      {         _state.emplace(std::forward<Xs>(xs)...);     } }; 

As you can see from the code above, the interface of context::initialize_state tells the user nothing about how to initialize context::_state. The user is forced to look at the implementation of initialize_state and then look at state::state to understand what should be passed to initialize_state.

I could change initialize_state to...

void initialize_state(A&& a, B&& b, C&& c)  {     _state.emplace(std::move(a), std::move(b), std::move(c)); } 

...but this has a major drawback: there is code duplication with state::state, that needs to be manually maintained in case the argument types change.

Is there any way I can get the best of both worlds (DRY and user-friendly interface)? Note that state is not movable/copyable.


The class state may not be copyable/movable, but it appears that A, B and C are. (So I'm assuming there is some other, internal data in state that prevents copyability/movability)

You can pull these members out into another class that can be injected into state. For lack of a better name, I'll call it state_args:

struct state_args {    explicit state_args(A a, B b, C c);    A a_;    B b_;    C c_; }; 

Which enables the following:

class context { private:     class state     {         state(state_args args);          state(const state&) = delete;         state(state&&) = delete;     };      std::optional<state> _state; // or `boost::optional`  public:     template<class STATE_ARGS, /*enable_if to ensure STATE_ARGS is indeed state_args*/>     void initialize_state(STATE_ARGS&& internal_state)      {         _state.emplace(std::forward<STATE_ARGS>(internal_state));     } }; 

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: