Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to populate a const member array based on a constructor argument?

Let's say I have this class:

template<int N>
class A {
    public:
    A(const char *s) ...
    private:
    const char buf[N];
};

The template is there so that I can configure the array size without dynamic memory allocation (a requirement). The buf member is const because it is intended to remain constant over the lifetime of the object after the object has been initialized.

To clarify, I also do not have access to the STL.

What are my options to define this constructor so that I can copy the contents of s into buf? One option is const_cast but I'm looking for alternatives that does not require this.

like image 828
Ana Avatar asked Mar 12 '23 15:03

Ana


1 Answers

The solution provided by @Richard Hodges requires the class be initialized with char array, as opposed to char const*, which changes the signature of the constructor. If that doesn't work for your case, then here is one solution which does not change the signature:

template<int N>
class A 
{
       template<size_t...Is>
       A(const char * s, std::index_sequence<Is...>)
        : _assert(std::strlen(s) <= N, "size of buffer is bigger than N"), 
          buf{ (Is, *s != '\0'? *s++: *s)... }
        {}

    public:
        A(const char *s) : A(s, std::make_index_sequence<N>()) {}

    private:
       throwable  _assert;
       const char  buf[N];
};

where throwable is defined as:

 struct throwable
 {
    throwable(bool b, char const * message){
      if (not b) 
          throw std::invalid_argument(message);
    }
 };

The use of throwable ensures that buf doesn't get initialized with buffer larger than N bytes. If however your situation ensures that and thus doesn't need this check, you could remove it. The code should work without it as well, though I'd suggest you to keep it at least in debug mode.

Note that the addition of _assert as member increases the size of the class at least by one byte. To avoid this, we could use empty base class optimiation and do this instead:

template<int N>
class A : private throwable
{
       template<size_t...Is>
       A(const char * s, std::index_sequence<Is...>)
        : throwable(std::strlen(s) <= N, "size of buffer is bigger than N"), 
          buf{ (Is, *s != '\0'? *s++: *s)... }
        {}
    public:
        A(const char *s) : A(s, std::make_index_sequence<N>()) {}

    private:
       const char  buf[N];
};

That saves us 1 byte per instance.

like image 93
Nawaz Avatar answered Apr 27 '23 00:04

Nawaz