How do I remove this inheritance-related code smell?


You don't need any virtuals or templates here. Just add a SomeInfo* pointer and its length to Base, and provide a protected constructor to initialize them (and since there's no default constructor, it won't be possible to forget to initialize them).


The constructor being protected is not a hard requirement, but since Base is not an abstract base class anymore, making the constructor protected prevents Base from being instantiated.


class Base
{
public:
    struct SomeInfo
    {
        const char *name;
        const f32_t value;
    };

    void iterateInfo()
    {
        for (int i = 0; i < c_info_len; ++i) {
            DPRINTF("Name: %s - Value: %f \n", c_info[i].name,
                     c_info[i].value);
        }
    }

protected:
    explicit Base(const SomeInfo* info, int len) noexcept
        : c_info(info)
        , c_info_len(len)
    { }

private:
    const SomeInfo* c_info;
    int c_info_len;
};

class DerivedA : public Base
{
public:
    DerivedA() noexcept
        : Base(c_myInfo, sizeof(c_myInfo) / sizeof(c_myInfo[0]))
    { }

private:
    const SomeInfo c_myInfo[2] { {"NameA1", 1.1f}, {"NameA2", 1.2f} };
};

class DerivedB : public Base
{
public:
    DerivedB() noexcept
        : Base(c_myInfo, sizeof(c_myInfo) / sizeof(c_myInfo[0]))
    { }

private:
    const SomeInfo c_myInfo[3] {
        {"NameB1", 2.1f},
        {"NameB2", 2.2f},
        {"NameB2", 2.3f}
    };
};

You can of course use a small, zero-overhead wrapper/adapter class instead of the c_info and c_info_len members in order to provide nicer and safer access (like begin() and end() support), but that's outside the scope of this answer.


As Peter Cordes pointed out, one issue with this approach is that the derived objects are now larger by the size of a pointer plus the size of an int if your final code still uses virtuals (virtual functions you haven't showed in your post.) If there's no virtuals anymore, then object size is only going to increase by an int. You did say that you're on a small embedded environment, so if a lot of these objects are going to be alive at the same time, then this might be something to worry about.


Peter also pointed out that since your c_myInfo arrays are const and use constant initializers, you might as well make them static. This will reduce the size of each derived object by the size of the array.

No comments:

Post a Comment

CPU vs GPU Architecture

  CPU vs GPU Architecture CPU (Central Processing Unit) and GPU (Graphics Processing Unit) have distinct architectural differences, optimize...

Best for you