4

I work on a memory constrained system where new/malloc calls are wrapped to fail at build time. So std::vector is not an acceptable solution, unfortunately.

I have an std::array member of a class where the size is known at compile time, but may vary between particular targets (e.g. from a config file) so I have access to something like constexpr size_t len = Config::ArrSize. I want my class to hold a std::array of objects, but these objects they do not have default constructors. I would much prefer to avoid two step initialization (e.g. implement a meaningless default ctor, and then pass them actual values later). I also know all the constructor values at compile time! I just can't find a clean way to convey this, since the length might vary between particular targets, but is known for any given garget.

Is there a way to cleanly convey this? e.g. I'd want something akin to

#include Config.h

constexpr size_t arr_size = Config::ArrSize;
constexpr size_t ctor_arg = Config::Arg;

class Foo {
  public:
  // line which doesn't work but demonstrates what I'd like
  Foo() : fooArr {Bar(ctor_arg)} {}
  private:
  std::array<Bar, arr_size> fooArr;

};

They will all be initialized the same way, and have a known size at compile time. They must be constructed upon initialization because of their lack of default ctors. Something like a std::fill but available at initialization. Yes I could defer it for a bit with pointers until the ctor body but that's ugly imho. How can I do this?

Bar doesn't have a constexper ctor but perhaps that might help?

brenzo
  • 677
  • 2
  • 6
  • 11
  • 1
    Is it viable to implement `Foo`'s default constructor in Config.cpp? http://coliru.stacked-crooked.com/a/bbbcba21c164cbdd – Mooing Duck Sep 09 '20 at 23:34
  • @MooingDuck: The would obviate the question, but good point! Usually better to avoid the problem in the first place. – einpoklum Sep 09 '20 at 23:36
  • @MooingDuck yes it is possible in my particular use case, but I think the question should stand for those who don't have the ability to modify the code they may have to use. – brenzo Sep 10 '20 at 00:21
  • @brenzo: My followup suggestion would be to put `#define CTOR_IMPL {Bar(ctor_arg), Bar(ctor_arg), Bar(ctor_arg)}` in the header – Mooing Duck Sep 10 '20 at 04:41

3 Answers3

3

Option 1: Use a named constructor idiom

We'll write a named constructor idiom, array_repeat(), which takes a single value and produces - at compile-time - an array with all values set to it. You can then write:

class Foo {
public:
  Foo() : fooArr {array_repeat<arr_size>(Bar(ctor_arg))} {}
private:
  std::array<Bar, arr_size> fooArr;
};

I've split off the implementation into a separate question and answer, here on SO.

Option 2: Use a vector with a custom allocator

std::vector has an additional template parameter - the allocator class. The default value is an allocator which uses new[] and delete[]. But - you could have an allocator that takes up a fixed buffer on the stack (or wherever), and use a resizable std::vector still. That would save you the need to pre-construct dummy values.

einpoklum
  • 118,144
  • 57
  • 340
  • 684
0

You could do something like this:

class Bar
{
public:
    Bar(someClass obj) { /*...*/ }
};

class Foo
{
public:
    template<class... T>
    Foo(T... obj) : fooArr {Bar(obj)...} {}
private:
    std::array<Bar, arr_size> fooArr;
}

while I don't know if that's the best way of doing it.

Ranoiaetep
  • 5,872
  • 1
  • 14
  • 39
  • Issue with this is that I believe I'd have to add template annotations to the entire `Foo` class, which is again really messy. :/ e.g. there would be `template<>` statements on the dozen or so methods in the `.cpp` file. – brenzo Sep 09 '20 at 23:26
  • @brenzo I believe you only need the template for the constructor, since your `std::array` already had a type in it. – Ranoiaetep Sep 09 '20 at 23:30
0

You could use braces initialization std::array<int,4> a = {1,2,3,4};... perhaps you could write a template function that initializes it without directly writing initializer list but I am uncertain how.

Generally, I don't advice using std::array when the type isn't default constructible. You could use type like boost::small_vector instead that has static capacity and dynamic size.

ALX23z
  • 4,456
  • 1
  • 11
  • 18
  • We don't use `boost` sadly, and I doubt I could make a case to have it pulled in for such a small change. The brace initialization would work but I can't make it generic for size `N`. I need a way to do this with the standard library afaik. – brenzo Sep 09 '20 at 23:28
  • @brenzo You can just copy implementation or make something similar. The class isn't complicated by any means. – ALX23z Sep 09 '20 at 23:32