0

I have a variadic template function

template<typename ...ARGS>
inline void ReadStream::decode(ARGS&...args) {
   internalDecode(args...);
}

template<typename T, typename ...ARGS>
inline void ReadStream::internalDecode(T &head, ARGS&...args) {
  read(head);
  internalDecode(args...);
}

  inline void ReadStream::internalDecode() {
  }

this allows me to write

int a, b;
ByteArray c;
String d

 x.decode(a,b,c,d);

to unpack an incoming binary stream into a set of variables with no boiler plate.

I have implemented a wrapper object 'FixedSize' that changes the default format that a particular variable is read/decoded in.

 template <typename T>
 class FixedSize{
     T &value;
     uint16_t  length;

  public:

     FixedSize(T &wrapped, uint32_t size):
       value(wrapped),
       length(static_cast<uint16_t>(size)){
     }

     template< typename STREAM>
     void read(STREAM &stream){
       stream.readBytes(value, 0, length);
       value.setLength(length);
     }

  };

  template<typename T>
  FixedSize<T> fixedSizeField(T &field, uint32_t length){
     return FixedSize<T>(field, length);
  }

This in theory would allow the calling of the decode like such

 x.decode(a,b,fixedSize(c,16), d);

However the object returned by fixedSize() is now a RValue and the compiler balks at allowing this with

invalid initialization of non-const reference of type 'FixedSize&' from an rvalue ....

Because the RValue holds a reference to the underlying object, this code would actually work if the compiler would allow me to compile it. In fact it works if I create an object of type FixedSize and then pass it to the decode function like so.

 auto e = fixedSize(c,16)
 x.decode(a,b, e, d)

How would I go about forcing the compiler to accept the wrapper object returned as a rvalue here?

Andrew Goedhart
  • 937
  • 9
  • 17
  • Try to use a universial reference with &&. This could let to complications in other situations but could solve your Problem here. – Raynigon Oct 06 '17 at 13:54

2 Answers2

1

Don't; forward the parameters everywhere (by taking Args&&... and via std::forward(args)...), except in head, and make an overload. Observe that the wrapper might be a const; so you can just take it as a const& and still modify the target via the reference.

Also, you do not need decode() and internalDecode separately as I see; just keep internalDecode() and name it decode().

lorro
  • 10,687
  • 23
  • 36
0

Based on the comments from Iorro this is the working code using universal references, forwarding and function overloading:

template<typename T, typename ...ARGS>
inline void ReadStream::decode(T &head, ARGS&&...args) {
  read(head);
  decode(std::forward<ARGS>(args)...);
}

template<typename T, typename ...ARGS>
inline void ReadStream::decode(const T &head, ARGS&&...args) {
  read(head);
  decode(std::forward<ARGS>(args)...);
} 

inline void ReadStream::decode() {
}

also the read function in the FixedWrapper needed to be made const i.e

 template< typename STREAM>
 void read(STREAM &stream) const {
   stream.readBytes(value, 0, length);
   value.setLength(length);
 }    

Now when calling

x.decode(a,b,fixedSize(c,16), d);

this works because the Rvalue can be passed to the overloaded function taking (const T&, ...) and everything compiles and runs.

Andrew Goedhart
  • 937
  • 9
  • 17