[OpenTRV-dev] C++11 fun on Arduino

Damon Hart-Davis damon at opentrv.uk
Sun Nov 20 20:17:31 GMT 2016

Hi Peeps,

This may all be blindingly obvious to some of you.

I’m basically trying to put a nice wrapper around our JSON stats output to clear up some of the cruft in the code base, ie just make it all look easy.

Basically to end up with just a:

    static auto sh = OTV0P2BASE::makeJSONStatsHolder(AmbLight, RelHumidity, … other Sensor instances … );

and then each time you want to send stats do:


and it will take care of doing all the relevant put() calls.

As ever the aim is to be as efficient as possible in terms of code and data size, for example, so the code below does what it can at compile time including statically allocating the data structures just large enough based on the template list.

Now, this all works beautifully on a machine with C++11 and a full STL implementation.  The Arduino IDE has the former but not the latter.

This isn’t very long, and I wonder if it could be simplified to take away the need for STL entirely.  I went cross-eyes getting it to work *with* STL!



// Helper class used to size the stats generator and easily extract sensor values for it.
// At least one sensor must be provided.
template<typename T1, typename ... Ts>
class JSONStatsHolder final
    std::tuple<T1, Ts...> args;

    // Number of arguments/stats.
    static constexpr size_t argCount = 1 + sizeof...(Ts);

    // JSON generator.
    typedef OTV0P2BASE::SimpleStatsRotation<argCount> ss_t;
    ss_t ss;

    // Construct an instance; though the template helper function is usually easier.
    template <typename... Args>
    constexpr JSONStatsHolder(Args&&... args) : args(std::forward<Args>(args)...)
      { static_assert(sizeof...(Args) > 0, "must take at least one arg"); }

    // Many thanks for template wrangling examples to David Godfrey and others:
    //     http://stackoverflow.com/questions/16868129/how-to-store-variadic-template-arguments
    //     http://stackoverflow.com/questions/8992853/terminating-function-template-recursion
    template<std::size_t> struct Int2Type { };
    // Put...
    // Ignore placeholder key or int entry.
    bool _putOrRemove(int) { return(true); }
    bool _putOrRemove(OTV0P2BASE::MSG_JSON_SimpleStatsKey_t) { return(true); }
    // Accept/put Sensor (don't over-constrain the arg type else SubSensor etc may not be handled correctly).
    template <class T> bool _putOrRemove(T &s) { return(ss.putOrRemove(s)); }
    template<typename ... Args> bool putOrRemove(Int2Type<0>, std::tuple<Args...>& tup)
        { return(_putOrRemove(std::get<0>(tup))); }
    template<size_t I, typename ... Args> bool putOrRemove(Int2Type<I>, std::tuple<Args...>& tup)
        { return(putOrRemove(Int2Type<I-1>(), tup) && _putOrRemove(std::get<I>(tup))); }
    // Read...
    template<typename ... Args> void read(Int2Type<0>, std::tuple<Args...>& tup)
        { return((std::get<0>(tup)).read()); }
    template<size_t I, typename ... Args> void read(Int2Type<I>, std::tuple<Args...>& tup)
        { read(Int2Type<I-1>(), tup); (std::get<I>(tup)).read(); }

    // Call read() on all sensors; usually done once, at initialisation.
    void readAll() { read(args); }
    // Put all the attached isAvailable() sensor values into the stats object; remove those !isAvailable().
    bool putOrRemoveAll() { return(putOrRemove(Int2Type<argCount-1>(), args)); }

// Helper function to avoid having to spell out the types explicitly.
// Pass a list of Sensors to makeJSONStatsHolder() to create a stats holder for them.
// (Key names of type MSG_JSON_SimpleStatsKey_t, or ints such as 0, can be used instead as placeholders,
// and will leave free space in the stats object, eg to manually put values of that name.)
// Use putOrRemoveAll() to put current values for all stats into the stats holder.
// Use readAll() to force a read() of all sensors, eg at start-up.
template <typename... Args>
constexpr JSONStatsHolder<Args...> makeJSONStatsHolder(Args&&... args)
    { return(JSONStatsHolder<Args...>(std::forward<Args>(args)...)); }

More information about the OpenTRV-dev mailing list