For a refactoring project, I'm writing C++11 code and compiling with g++ on an Ubuntu 18.x system. For illustration, I have created a small toy-project to explain it.
I have a class Wall
and a class Brick
. Wall has a member std::array<Brick, sz>
where Brick has its default, copy, and move constructors and assignment operators deleted. The size of the array is known at compile time, but it should be possible to change it by only changing the sz
-variable and therefore an initializer list is not an option. Additionally, the code for Brick must not be refactored due to copyright/legal issues.
As requested, I will elaborate a bit more on the project. Currently the Brick instances are each assigned to a single Brick variable, like this:
Brick brick0 {"number 0"};
Brick brick1 {"number 1"};
Brick brick2 {"number 2"};
Part of the refactoring task is to make it easier to increase the size of the 'wall' by only changing a single variable, instead of introducing new brickXX variables. Additionally, I can say that the number of brickXXs that needs to be defined is detailed in an xml-file that has been auto-generated from synthesizing a hardware design from VHDL code. Also, there are not just bricks but also the analogy of windows, doors, lamps, and much more. In fact something like 1200 lines of individual object variables.
These limitations creates a problem with the initialization of the Brick objects in the array, but I cooked up something that I hope could be a possible solution.
My idea is to extend Brick with a class Brick_ex
that implements a templated default constructor for supplying a function pointer to a count function. However, it seems that I'm missing something to finish it up and (hopefully) have it compile.
class Brick
{
public:
const string valStr;
explicit Brick(const string& valStrInit)
: valStr{valStrInit}
{}
Brick() = delete;
Brick(const Brick &) = delete;
Brick(Brick &&) = delete;
Brick &operator=(const Brick &) = delete;
Brick &operator=(Brick &&) = delete;
};
template <int (*F)()>
class Brick_ex : public Brick
{
public:
Brick_ex<F>()
: Brick{"number " + to_string((*F)())}
{}
};
class Wall
{
int val;
public:
int count() { return val++; }
private:
array<Brick_ex<count>, 10> bricks;
public:
Wall()
: val{42}, bricks{}
{
for (auto &brick : bricks)
cout << brick.valStr << endl;
}
};
main(void)
{
Wall a_wall;
}
When compiling I get several errors, but I think the following are the important ones:
main.cpp:41:26: error: invalid use of non-static member function ‘int Wall::count()’
array<Brick_ex<count>, 10> arr;
^
main.cpp:39:9: note: declared here
int count() { return val++; }
^~~~~
I understand that using this non-static member function is a problem, because it will access the val variable of some instance that is unknown at compile time. But is there another way to define a template function that can dereference instantiated objects variables? Is there another way to achieve it?
main.cpp:41:26: error: could not convert template argument ‘Wall::count’ from ‘int (Wall::)()’ to ‘int (*)()’
array<Brick_ex<count>, 10> bricks;
^
main.cpp:41:31: error: template argument 1 is invalid
array<Brick_ex<count>, 10> bricks;
^
To me this looks like some syntax issue, so what is the correct syntax? - or is it something completely different?!?
Alternatively, instead of using a count function dereferencing an instance variable, I have thought about the possibility to use the index of each Brick_ex object instantiation. But I also haven't succeeded in figuring out how to do this?
Hope some of you can help me out...
Changelog:
Refactored class and array names -> Thank you JohnFilleau for pointing out the errors and suggesting the Wall/Brick analogy. :-)