1

Currently I'm struggling with something like this:

class Command final {
    const std::vector<uint8_t> cmd;
public:
    constexpr Command(const std::initializer_list<uint8_t> cmd_)
        :cmd(cmd_)
        {}
    void copyToWithSlightModifications(uint8_t* buf, size_t len) { /*...*/ }
    std::string getSomeInformation() { /*...*/ }
}

const Command cmd1({ 0x01, 0x02, 0x03 });
const Command cmd2({ 0x01, 0x02, 0x03, 0x04 });
// lots more of these

Of course, std::vector is not something that works here, but it's here as it expresses my intent best. I tried std::array and a few other ideas, but also have failed.

Background: this is for embedded development, so I'm tight on resources and consting things tends to put them to flash where memory is cheap. Doing it as a real vector will put this into scarce RAM memory. This is on gcc version 5.2.0.

TL;DR: I need to have a variable length container that I can put as a field on a const object with and initialize it in constexpr context. Any idea what that might look like? Doesn't have to be anything fancy, if it's a uint8_t* and a size_t with it's size, that will work too.

Krzysztof Bociurko
  • 4,575
  • 2
  • 26
  • 44

2 Answers2

3

Going off of your description and the code snippet, this seems to fit the criteria.

template<size_t N>
class Command
{
    uint8_t cmd[N];
public:
    template<typename... Args>
    constexpr Command(Args... args) : cmd{uint8_t(args)...}
    {
        static_assert(sizeof...(Args) == N,
                      "argument count mismatch");
    }
};

constexpr Command<4> cmd4{0, 1, 2, 3};
constexpr Command<2> cmd2{0, 1};

You may also write a helper function to avoid explicitly typing the argument count

template<typename... Args>
constexpr Command<sizeof...(Args)> make_command(Args... args)
{
    return {args...};
}

constexpr auto cmd4 = make_command(0, 1, 2, 3);
constexpr auto cmd2 = make_command(0, 1);

Alternatively, in C++17 there is template deduction guides

template<typename... Args>
Command(Args...) -> Command<sizeof...(Args)>;

constexpr Command cmd4{0, 1, 2, 3};
constexpr Command cmd2{0, 1};

Note however, a different sized Command is a different type, meaning they won't fit in a container.

Passer By
  • 19,325
  • 6
  • 49
  • 96
  • Thank you, I have managed to use your solution with slight modification and introduction of a base interface class. For anyone interested here's a minimal example: https://ideone.com/Mg32QW – Krzysztof Bociurko Nov 22 '17 at 09:45
  • One more thing - my compiler has one issue with this answer, namely I can do `Command c1{ uint8_t{1}, uint8_t{2} }` but not `Command c2{0x01, 0x02}` because of `narrowing conversion of 'args#...' from 'int' to 'uint8_t {aka unsigned char}' inside { }` - any ideas on this one, @Passer By? – Krzysztof Bociurko Nov 22 '17 at 10:23
  • For the pre-C++17 case, you can add a point of deduction: `auto make_command(Args&&... args) -> Command { return Command(std::forward(args...); }` – Caleth Nov 22 '17 at 10:28
  • 1
    @KrzysztofBociurko The literals are deduced to have type `int`, and due to the limitations of the compilers warnings, it will still warn you of narrowing conversions even when it is known at compile time the values won't overflow. – Passer By Nov 22 '17 at 16:19
  • @Caleth Yes of course, dunno why I didn't think of it. Thanks. – Passer By Nov 22 '17 at 16:21
  • 1
    @KrzysztofBociurko I added in an explicit cast to remedy that. – Passer By Nov 22 '17 at 16:25
  • @PasserBy my compiler required adding a `constexpr` to `make_command`, but apart from this small issue, this is exactly what was needed, thanks again! – Krzysztof Bociurko Nov 23 '17 at 08:40
0

In deeply embedded you usually use circular buffers so solve this kind of problem. They have a fixed maximal size but depending on implementation they behave just like lists. Unfortunately the standard c++ library does not have any circular buffers but there are plenty of tutorials on the web.

I doublt you can use boost library on your restricted system but in case you do you can simply use boost::circular_buffer

Some other pages that might help you implement an circular buffer suitable for you might be this or this

Detonar
  • 1,409
  • 6
  • 18
  • Don't understand why a circular buffer might of use here. Circular buffers are for FIFOs and such where data ordered is constantly moving - and this ought to be a storage class that is not changing at any time. Could you clarify? – Krzysztof Bociurko Nov 22 '17 at 09:11
  • You can perform random access on elements of a ring buffer. You may give them a large enough fixed size and use it as if it were dynamic. – Detonar Nov 22 '17 at 09:26
  • So you mean using a circular buffer as a non-circular plain old static array with pointers to specific places in it? Or is there any hidden benefit of it's added complexity? – Krzysztof Bociurko Nov 22 '17 at 09:40
  • Also, as far as I can tell, there will be an issue with properly initiating it. All will have to be done by hand, including counting the offsets of separate commands. – Krzysztof Bociurko Nov 22 '17 at 09:42
  • It seems i misunderstood your question. I got it that you want a data structure with a variable count of elements but statically initialized. If this isn't what your question was about i'm gonna delete this answer. – Detonar Nov 22 '17 at 11:04