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?

4
  • 1
    Is it viable to implement Foo's default constructor in Config.cpp? coliru.stacked-crooked.com/a/bbbcba21c164cbdd Commented Sep 9, 2020 at 23:34
  • @MooingDuck: The would obviate the question, but good point! Usually better to avoid the problem in the first place. Commented Sep 9, 2020 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. Commented Sep 10, 2020 at 0:21
  • @brenzo: My followup suggestion would be to put #define CTOR_IMPL {Bar(ctor_arg), Bar(ctor_arg), Bar(ctor_arg)} in the header Commented Sep 10, 2020 at 4:41

3 Answers 3

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.

Sign up to request clarification or add additional context in comments.

5 Comments

Just wondering, how did you made the link look like a button?
@Ranoiaetep: Click the "Edit" button below my post and find out by reading the source!
Been trying to, keep showing "edit queue is full" :D
@Ranoiaetep: Try <kbd> and </kbd> within the link brackes.
Option 1 satisfies my needs but it's a little verbose for a simple concept. Not that it's necessarily a bad thing but I am a bit surprised this appears to be the cleanest way to resolve this. Option 2 is very interesting and arguably even more obscure, but is probably more readable. Thank you!
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.

2 Comments

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 You can just copy implementation or make something similar. The class isn't complicated by any means.
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.

2 Comments

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 I believe you only need the template for the constructor, since your std::array already had a type in it.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.