How do I remove this inheritance-related code smell?How do you set, clear, and toggle a single bit?Prefer composition over inheritance?How do I iterate over the words of a string?How can I profile C++ code running on Linux?Difference between private, public, and protected inheritancePython class inherits objectC++11 introduced a standardized memory model. What does it mean? And how is it going to affect C++ programming?What are the nuances of scope prototypal / prototypical inheritance in AngularJS?Why not inherit from List<T>?Downcasting best-practice (C++)

What is "industrial ethernet"?

Umlaut character order when sorting

Why isn't it a compile-time error to return a nullptr as a std::string?

Is "Busen" just the area between the breasts?

Is there a name for the trope when there is a moments dialogue when someone pauses just before they leave the room?

Do I have to explain the mechanical superiority of the player-character within the fiction of the game?

Is declining an undergraduate award which causes me discomfort appropriate?

Designing a magic-compatible polearm

What is the "ls" directory in my home directory?

Has a life raft ever been successfully deployed on a modern commercial flight?

How does DC work with natural 20?

A word for delight at someone else's failure?

Subtract the Folded Matrix

Justifying Affordable Bespoke Spaceships

How do I professionally let my manager know I'll quit over an issue?

Going back in time in and initial value problem

What happened to Hopper's girlfriend in season one?

Helping ease my back pain by studying 13 hours everyday , even weekends

In Mistborn, why can metal in people's bodies be affected by Allomancy sometimes?

Dates on degrees don’t make sense – will people care?

Boss wants someone else to lead a project based on the idea I presented to him

In the US, can a former president run again?

Is there a term for the belief that "if it's legal, it's moral"?

How do internally carried IR missiles acquire a lock?



How do I remove this inheritance-related code smell?


How do you set, clear, and toggle a single bit?Prefer composition over inheritance?How do I iterate over the words of a string?How can I profile C++ code running on Linux?Difference between private, public, and protected inheritancePython class inherits objectC++11 introduced a standardized memory model. What does it mean? And how is it going to affect C++ programming?What are the nuances of scope prototypal / prototypical inheritance in AngularJS?Why not inherit from List<T>?Downcasting best-practice (C++)






.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty height:90px;width:728px;box-sizing:border-box;








16















I need to implement a lot of derived classes with different const member data. The data processing should be handled in the base class, but I can't find an elegant way to access the derived data. The code below is working, but I really don't like it.



The code needs to run in a small embedded environment so extensive usage of heap or fancy libraries like Boost are no option.



class Base

public:
struct SomeInfo

const char *name;
const f32_t value;
;

void iterateInfo()

// I would love to just write
// for(const auto& info : c_myInfo) ...

u8_t len = 0;
const auto *returnedInfo = getDerivedInfo(len);
for (int i = 0; i < len; i++)

DPRINTF("Name: %s - Value: %f n", returnedInfo[i].name, returnedInfo[i].value);


virtual const SomeInfo* getDerivedInfo(u8_t &length) = 0;
;

class DerivedA : public Base

public:
const SomeInfo c_myInfo[2] "NameA1", 1.1f, "NameA2", 1.2f ;

virtual const SomeInfo* getDerivedInfo(u8_t &length) override

// Duplicated code in every derived implementation....
length = sizeof(c_myInfo) / sizeof(c_myInfo[0]);
return c_myInfo;

;

class DerivedB : public Base

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

virtual const SomeInfo *getDerivedInfo(u8_t &length) override

// Duplicated code in every derived implementation....
length = sizeof(c_myInfo) / sizeof(c_myInfo[0]);
return c_myInfo;

;

DerivedA instanceA;
DerivedB instanceB;
instanceA.iterateInfo();
instanceB.iterateInfo();









share|improve this question









New contributor



SirNobbyNobbs is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.



















  • Is Base supposed to be able to be instantiable directly, or only the derived types?

    – Nikos C.
    18 hours ago












  • No, I only instanciate the derived classes.

    – SirNobbyNobbs
    18 hours ago











  • @NikosC.Base is abstract, can't create instances of it.

    – Tanveer Badar
    18 hours ago






  • 2





    If SomeInfo c_myInfo[3] is const and has a compile-time constant initializer, why do you have it inside the object instead of static? Do you only create one instance of each type, so there isn't actually duplication of the pointers + floats? (Also a string key/value array doesn't sound great for efficiency if you're using it as a dictionary, but that's a separate issue. Sounds like a job for enum..)

    – Peter Cordes
    6 hours ago

















16















I need to implement a lot of derived classes with different const member data. The data processing should be handled in the base class, but I can't find an elegant way to access the derived data. The code below is working, but I really don't like it.



The code needs to run in a small embedded environment so extensive usage of heap or fancy libraries like Boost are no option.



class Base

public:
struct SomeInfo

const char *name;
const f32_t value;
;

void iterateInfo()

// I would love to just write
// for(const auto& info : c_myInfo) ...

u8_t len = 0;
const auto *returnedInfo = getDerivedInfo(len);
for (int i = 0; i < len; i++)

DPRINTF("Name: %s - Value: %f n", returnedInfo[i].name, returnedInfo[i].value);


virtual const SomeInfo* getDerivedInfo(u8_t &length) = 0;
;

class DerivedA : public Base

public:
const SomeInfo c_myInfo[2] "NameA1", 1.1f, "NameA2", 1.2f ;

virtual const SomeInfo* getDerivedInfo(u8_t &length) override

// Duplicated code in every derived implementation....
length = sizeof(c_myInfo) / sizeof(c_myInfo[0]);
return c_myInfo;

;

class DerivedB : public Base

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

virtual const SomeInfo *getDerivedInfo(u8_t &length) override

// Duplicated code in every derived implementation....
length = sizeof(c_myInfo) / sizeof(c_myInfo[0]);
return c_myInfo;

;

DerivedA instanceA;
DerivedB instanceB;
instanceA.iterateInfo();
instanceB.iterateInfo();









share|improve this question









New contributor



SirNobbyNobbs is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.



















  • Is Base supposed to be able to be instantiable directly, or only the derived types?

    – Nikos C.
    18 hours ago












  • No, I only instanciate the derived classes.

    – SirNobbyNobbs
    18 hours ago











  • @NikosC.Base is abstract, can't create instances of it.

    – Tanveer Badar
    18 hours ago






  • 2





    If SomeInfo c_myInfo[3] is const and has a compile-time constant initializer, why do you have it inside the object instead of static? Do you only create one instance of each type, so there isn't actually duplication of the pointers + floats? (Also a string key/value array doesn't sound great for efficiency if you're using it as a dictionary, but that's a separate issue. Sounds like a job for enum..)

    – Peter Cordes
    6 hours ago













16












16








16


3






I need to implement a lot of derived classes with different const member data. The data processing should be handled in the base class, but I can't find an elegant way to access the derived data. The code below is working, but I really don't like it.



The code needs to run in a small embedded environment so extensive usage of heap or fancy libraries like Boost are no option.



class Base

public:
struct SomeInfo

const char *name;
const f32_t value;
;

void iterateInfo()

// I would love to just write
// for(const auto& info : c_myInfo) ...

u8_t len = 0;
const auto *returnedInfo = getDerivedInfo(len);
for (int i = 0; i < len; i++)

DPRINTF("Name: %s - Value: %f n", returnedInfo[i].name, returnedInfo[i].value);


virtual const SomeInfo* getDerivedInfo(u8_t &length) = 0;
;

class DerivedA : public Base

public:
const SomeInfo c_myInfo[2] "NameA1", 1.1f, "NameA2", 1.2f ;

virtual const SomeInfo* getDerivedInfo(u8_t &length) override

// Duplicated code in every derived implementation....
length = sizeof(c_myInfo) / sizeof(c_myInfo[0]);
return c_myInfo;

;

class DerivedB : public Base

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

virtual const SomeInfo *getDerivedInfo(u8_t &length) override

// Duplicated code in every derived implementation....
length = sizeof(c_myInfo) / sizeof(c_myInfo[0]);
return c_myInfo;

;

DerivedA instanceA;
DerivedB instanceB;
instanceA.iterateInfo();
instanceB.iterateInfo();









share|improve this question









New contributor



SirNobbyNobbs is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.











I need to implement a lot of derived classes with different const member data. The data processing should be handled in the base class, but I can't find an elegant way to access the derived data. The code below is working, but I really don't like it.



The code needs to run in a small embedded environment so extensive usage of heap or fancy libraries like Boost are no option.



class Base

public:
struct SomeInfo

const char *name;
const f32_t value;
;

void iterateInfo()

// I would love to just write
// for(const auto& info : c_myInfo) ...

u8_t len = 0;
const auto *returnedInfo = getDerivedInfo(len);
for (int i = 0; i < len; i++)

DPRINTF("Name: %s - Value: %f n", returnedInfo[i].name, returnedInfo[i].value);


virtual const SomeInfo* getDerivedInfo(u8_t &length) = 0;
;

class DerivedA : public Base

public:
const SomeInfo c_myInfo[2] "NameA1", 1.1f, "NameA2", 1.2f ;

virtual const SomeInfo* getDerivedInfo(u8_t &length) override

// Duplicated code in every derived implementation....
length = sizeof(c_myInfo) / sizeof(c_myInfo[0]);
return c_myInfo;

;

class DerivedB : public Base

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

virtual const SomeInfo *getDerivedInfo(u8_t &length) override

// Duplicated code in every derived implementation....
length = sizeof(c_myInfo) / sizeof(c_myInfo[0]);
return c_myInfo;

;

DerivedA instanceA;
DerivedB instanceB;
instanceA.iterateInfo();
instanceB.iterateInfo();






c++ c++11 inheritance






share|improve this question









New contributor



SirNobbyNobbs is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.










share|improve this question









New contributor



SirNobbyNobbs is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.








share|improve this question




share|improve this question








edited 3 hours ago









Peter Mortensen

14.2k1988114




14.2k1988114






New contributor



SirNobbyNobbs is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.








asked 19 hours ago









SirNobbyNobbsSirNobbyNobbs

815




815




New contributor



SirNobbyNobbs is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.




New contributor




SirNobbyNobbs is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.














  • Is Base supposed to be able to be instantiable directly, or only the derived types?

    – Nikos C.
    18 hours ago












  • No, I only instanciate the derived classes.

    – SirNobbyNobbs
    18 hours ago











  • @NikosC.Base is abstract, can't create instances of it.

    – Tanveer Badar
    18 hours ago






  • 2





    If SomeInfo c_myInfo[3] is const and has a compile-time constant initializer, why do you have it inside the object instead of static? Do you only create one instance of each type, so there isn't actually duplication of the pointers + floats? (Also a string key/value array doesn't sound great for efficiency if you're using it as a dictionary, but that's a separate issue. Sounds like a job for enum..)

    – Peter Cordes
    6 hours ago

















  • Is Base supposed to be able to be instantiable directly, or only the derived types?

    – Nikos C.
    18 hours ago












  • No, I only instanciate the derived classes.

    – SirNobbyNobbs
    18 hours ago











  • @NikosC.Base is abstract, can't create instances of it.

    – Tanveer Badar
    18 hours ago






  • 2





    If SomeInfo c_myInfo[3] is const and has a compile-time constant initializer, why do you have it inside the object instead of static? Do you only create one instance of each type, so there isn't actually duplication of the pointers + floats? (Also a string key/value array doesn't sound great for efficiency if you're using it as a dictionary, but that's a separate issue. Sounds like a job for enum..)

    – Peter Cordes
    6 hours ago
















Is Base supposed to be able to be instantiable directly, or only the derived types?

– Nikos C.
18 hours ago






Is Base supposed to be able to be instantiable directly, or only the derived types?

– Nikos C.
18 hours ago














No, I only instanciate the derived classes.

– SirNobbyNobbs
18 hours ago





No, I only instanciate the derived classes.

– SirNobbyNobbs
18 hours ago













@NikosC.Base is abstract, can't create instances of it.

– Tanveer Badar
18 hours ago





@NikosC.Base is abstract, can't create instances of it.

– Tanveer Badar
18 hours ago




2




2





If SomeInfo c_myInfo[3] is const and has a compile-time constant initializer, why do you have it inside the object instead of static? Do you only create one instance of each type, so there isn't actually duplication of the pointers + floats? (Also a string key/value array doesn't sound great for efficiency if you're using it as a dictionary, but that's a separate issue. Sounds like a job for enum..)

– Peter Cordes
6 hours ago





If SomeInfo c_myInfo[3] is const and has a compile-time constant initializer, why do you have it inside the object instead of static? Do you only create one instance of each type, so there isn't actually duplication of the pointers + floats? (Also a string key/value array doesn't sound great for efficiency if you're using it as a dictionary, but that's a separate issue. Sounds like a job for enum..)

– Peter Cordes
6 hours ago












8 Answers
8






active

oldest

votes


















14














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.






share|improve this answer




















  • 1





    This grows the size of every object by 1 pointer + 1 int. If you have many more instances of these objects than you do derived types, the OP's solution uses less total memory (data + code). They mention they're in a memory-constrained embedded environment. On a high-end system, the extra level of indirection is still a downside. (At least the pointer will typically be in the same cache line as the start of the data and the vtable pointer, so the extra latency is just 1 L1d cache ~load-use latency. Not an extra cache miss)

    – Peter Cordes
    6 hours ago












  • @PeterCordes: Note that the OPs solution grows the size of every object by ??? due to the virtual methods. That's probably 1 pointer, but it's worth noting that the size regression is smaller than it first appears. This also has fewer indirections, since we can directly deference the given pointer, wheras the OP's code has to dereference the virtual method, then call that to get a pointer, then dereference the pointer.

    – Mooing Duck
    3 hours ago












  • @MooingDuck: (successful) Branch prediction / speculation hides the latency of the function pointer, but yes it's a pretty terrible choice if you don't need polymorphism. It's plausible that polymorphism merely for the purpose of code-reuse is worth it if they're very memory-constrained, but likely having callers that statically know the type pass the right args to generic functions is the way to go, maybe by writing simple non-virtual inline helper functions that pass a pointer+size at each call site, or whatever other way you get a C++ compiler to emit machine code that does that.

    – Peter Cordes
    3 hours ago



















6














You could make Base a template and take the length of your const array. Something like this:



template<std::size_t Length>
class Base

public:
struct SomeInfo

const char *name;
const float value;
;

const SomeInfo c_myInfo[Length];

void iterateInfo()

//I would love to just write
for(const auto& info : c_myInfo)
// work with info


;


And then initialize the array accordingly from each base class:



class DerivedA : public Base<2>

public:
DerivedA() : Base<2> SomeInfo"NameA1", 1.1f, "NameA2", 1.2f
;

class DerivedB : public Base<3>

public:
DerivedB() : Base<3> SomeInfo"NameB1", 2.1f, "NameB2", 2.2f, "NameB2", 2.3f
;


And then use as you normally would. This method removes the polymorphism and uses no heap allocation (e.g. no std::vector), just as user SirNobbyNobbs requested.






share|improve this answer
































    3














    You can use CRTP:



    template<class Derived>
    class impl_getDerivedInfo
    :public Base


    virtual const SomeInfo *getDerivedInfo(u8_t &length) override

    //Duplicated code in every derived implementation....
    auto& self = static_cast<Derived&>(*this);
    length = sizeof(self.c_myInfo) / sizeof(self.c_myInfo[0]);
    return self.c_myInfo;

    ;


    class DerivedA : public impl_getDerivedInfo<DerivedA>

    public:
    const SomeInfo c_myInfo[2] "NameA1", 1.1f, "NameA2", 1.2f ;
    ;

    class DerivedB : public impl_getDerivedInfo<DerivedB>

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

    ;





    share|improve this answer























    • Does getDerivedInfo still need to be virtual here? Also you could turn the duplicated code into just a simple caller for a base-class generic function that takes pointer + length. That would give you clean syntax for a way to get the compiler to pass pointer+length to a common non-duplicated implementation.

      – Peter Cordes
      2 hours ago


















    2














    One way with C++17 would be to return a "view" object representing your content list. This can then be used in a C++11 for statement. You could write a base function that converts start+len into a view, so you don't need to add to the virtual method cruft.



    It is not that difficult to create a view object that is compatible with C++11 for statement. Alternatively, you could consider using the C++98 for_each templates that can take a begin and end iterator: Your start iterator is start; the end iterator is start+len.






    share|improve this answer
































      1














      Okay then let's simplify all the unnecessary complications :)



      Your code really boils down to the following:



      SomeInfo.h



      struct SomeInfo

      const char *name;
      const f32_t value;
      ;

      void processData(const SomeInfo* c_myInfo, u8_t len);


      SomeInfo.cpp



      #include "SomeInfo.h"

      void processData(const SomeInfo* c_myInfo, u8_t len)

      for (u8_t i = 0; i < len; i++)

      DPRINTF("Name: %s - Value: %f n", c_myInfo[i].name, c_myInfo[i].value);




      data.h



      #include "SomeInfo.h"

      struct A

      const SomeInfo info[2] "NameA1", 1.1f, "NameA2", 1.2f ;
      static const u8_t len = 2;
      ;

      struct B

      const SomeInfo info[3] "NameB1", 2.1f, "NameB2", 2.2f, "NameB2", 2.3f ;
      static const u8_t len = 3;
      ;


      main.cpp



      #include "data.h"

      int
      main()

      A a;
      B b;
      processData(a.info, A::len);
      processData(b.info, B::len);






      share|improve this answer

























      • Given the provided example you are right.But this is just a simplification to highlight my problem. The base class is already several hundred lines of code and every derived class also has much more inside than just the const info.

        – SirNobbyNobbs
        18 hours ago






      • 5





        Well, I can imagine. All I can suggest is to use composition instead of inheritance along with simple functions. Coding can be a simple and pleasurable experience. We just complicate everything for some reason :)

        – Adam Zahran
        17 hours ago






      • 2





        @SirNobbyNobbs "The base class is already several hundred lines of code" - That is more of a code smell than anything that your simplified example might highlight,

        – Goyo
        7 hours ago







      • 3





        There's no point having a u8_t len = 3; member in each struct; the length of the info[] array member is already statically known as part of the derived type. @SirNobbyNobbs: What you could do is have a small inline wrapper in each of A and B that passes the right args to a common processData function. It can be virtual if you need it to be, but letting it inline into each call site when you have full type info is good. (final on the derived type functions allows that in more cases.)

        – Peter Cordes
        6 hours ago











      • @PeterCordes Well I thought of that. But in order to retrieve the size we would need a function. This function could either be templated or repeated. But since I'm going for simplicity and avoiding code generation here I decided a little extra u8_t in the struct will harm no one :)

        – Adam Zahran
        6 hours ago


















      1














      You can move your data into a two-dimensional array outside of the classes and have each class return an index which contains relevant data.



      struct SomeInfo

      const char *name;
      const f32_t value;
      ;

      const vector<vector<SomeInfo>> masterStore
      "NameA1", 1.1f, "NameA2", 1.2f,
      "NameB1", 2.1f, "NameB2", 2.2f, "NameB2", 2.3f
      ;

      class Base

      public:
      void iterateInfo()

      // I would love to just write
      // for(const auto& info : c_myInfo) ...

      u8_t len = 0;
      auto index(getIndex());
      for(const auto& data : masterStore[index])

      DPRINTF("Name: %s - Value: %f n", data.name, data.value);


      virtual int getIndex() = 0;
      ;

      class DerivedA : public Base

      public:

      int getIndex() override

      return 0;

      ;

      class DerivedB : public Base

      public:

      int getIndex() override

      return 1;

      ;

      DerivedA instanceA;
      DerivedB instanceB;
      instanceA.iterateInfo();
      instanceB.iterateInfo();





      share|improve this answer




















      • 3





        Why a const std::vector for compile-time-constant data with known fixed size? Seems like a job for a flat 1D std::array<SomeInfo> with each derived class knowing the right start index + offset. (GetIndex returns a std::pair<int,int>). Or a std::array<std::vector<SomeInfo>>. Or maybe implicit lengths by using a flat array of SomeInfo objects with nullptr terminators for the end of each sub-array, if you only ever want to iterate in order.

        – Peter Cordes
        6 hours ago


















      1














      Just make the virtual function return a reference to the data directly (you need to change to vector then - not possible with array or C style array types with different sizes):



      virtual const std::vector<SomeInfo>& getDerivedInfo() = 0;


      or if pointers are the only feasible option, as a pointer range (iterators/range adapter would be preferred though if possible - more on that):



      virtual std::pair<SomeInfo*, SomeInfo*> getDerivedInfo() = 0;


      To make this last method work with range-based for loop: one way is to make a small 'range view' type that has the functions begin()/end() - essential a pair with begin()/end()



      Example:



      template<class T>
      struct ptr_range
      std::pair<T*, T*> range_;
      auto begin()return range_.begin();
      auto end()return range_.end();
      ;


      Then construct it with:



      virtual const ptr_range<SomeInfo> getDerivedInfo() override

      return std::begin(c_myInfo), std::end(c_myInfo);



      It is easy to make it non-template if a template is not desired.






      share|improve this answer
































        1














        Start with a vocabulary type:



        template<class T>
        struct span
        T* b = nullptr;
        T* e = nullptr;
        span( T* s, T* f ):b(s), e(f)
        span( T* s, size_t l ):span(s, s+l)
        template<size_t N>
        span( T(&arr)[N] ):span(arr, N)
        T* begin() const return b;
        T* end() const return e;
        size_t size() const return end()-begin();
        bool empty() const return size()==0;
        T& front() const return *begin();
        T& back() const return *(end()-1);
        ;

        // This is just here for the other array ctor:
        template<class T>
        struct span<T const>
        T const* b = nullptr;
        T const* e = nullptr;
        span( T const* s, T const* f ):b(s), e(f)
        span( T const* s, size_t l ):span(s, s+l)
        template<size_t N>
        span( T const(&arr)[N] ):span(arr, N)
        template<size_t N>
        span( T(&arr)[N] ):span(arr, N)
        T const* begin() const return b;
        T const* end() const return e;
        size_t size() const return end()-begin();
        bool empty() const return size()==0;
        T const& front() const return *begin();
        T const& back() const return *(end()-1);
        ;


        Now we can talk about a span<char>:



        class Base

        public:
        void iterateInfo()

        for(const auto& info : c_mySpan)
        DPRINTF("Name: %s - Value: %f n", info.name, info.value);


        private:
        span<const char> c_mySpan;
        Base( span<const char> s ):c_mySpan(s)
        Base(Base const&)=delete; // probably unsafe
        ;


        Now your derived looks like:



        class DerivedA : public Base

        public:
        const SomeInfo c_myInfo[2] "NameA1", 1.1f, "NameA2", 1.2f ;
        DerivedA() : Base(c_myInfo)
        ;


        This has overhead of two pointers per Base. A vtable uses one pointer, makes your type abstract, adds indirection, and adds one global vtable per Derived type.



        Now, in theory, you could get the overhead of this down to the length of the array, and presume that the array data starts right after Base, but that is fragile, non-portable and only useful if desperate.



        While you may be rightly leery of templates in embedded code (as you should be of any kind of code generation; code generation means you can generate more than O(1) binary from O(1) code). The span vocabulary type is compact and should be inlined to nothing if your compiler settings are reasonably aggressive.






        share|improve this answer

























          Your Answer






          StackExchange.ifUsing("editor", function ()
          StackExchange.using("externalEditor", function ()
          StackExchange.using("snippets", function ()
          StackExchange.snippets.init();
          );
          );
          , "code-snippets");

          StackExchange.ready(function()
          var channelOptions =
          tags: "".split(" "),
          id: "1"
          ;
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function()
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled)
          StackExchange.using("snippets", function()
          createEditor();
          );

          else
          createEditor();

          );

          function createEditor()
          StackExchange.prepareEditor(
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: true,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: 10,
          bindNavPrevention: true,
          postfix: "",
          imageUploader:
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          ,
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          );



          );






          SirNobbyNobbs is a new contributor. Be nice, and check out our Code of Conduct.









          draft saved

          draft discarded


















          StackExchange.ready(
          function ()
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f56627438%2fhow-do-i-remove-this-inheritance-related-code-smell%23new-answer', 'question_page');

          );

          Post as a guest















          Required, but never shown

























          8 Answers
          8






          active

          oldest

          votes








          8 Answers
          8






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes









          14














          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.






          share|improve this answer




















          • 1





            This grows the size of every object by 1 pointer + 1 int. If you have many more instances of these objects than you do derived types, the OP's solution uses less total memory (data + code). They mention they're in a memory-constrained embedded environment. On a high-end system, the extra level of indirection is still a downside. (At least the pointer will typically be in the same cache line as the start of the data and the vtable pointer, so the extra latency is just 1 L1d cache ~load-use latency. Not an extra cache miss)

            – Peter Cordes
            6 hours ago












          • @PeterCordes: Note that the OPs solution grows the size of every object by ??? due to the virtual methods. That's probably 1 pointer, but it's worth noting that the size regression is smaller than it first appears. This also has fewer indirections, since we can directly deference the given pointer, wheras the OP's code has to dereference the virtual method, then call that to get a pointer, then dereference the pointer.

            – Mooing Duck
            3 hours ago












          • @MooingDuck: (successful) Branch prediction / speculation hides the latency of the function pointer, but yes it's a pretty terrible choice if you don't need polymorphism. It's plausible that polymorphism merely for the purpose of code-reuse is worth it if they're very memory-constrained, but likely having callers that statically know the type pass the right args to generic functions is the way to go, maybe by writing simple non-virtual inline helper functions that pass a pointer+size at each call site, or whatever other way you get a C++ compiler to emit machine code that does that.

            – Peter Cordes
            3 hours ago
















          14














          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.






          share|improve this answer




















          • 1





            This grows the size of every object by 1 pointer + 1 int. If you have many more instances of these objects than you do derived types, the OP's solution uses less total memory (data + code). They mention they're in a memory-constrained embedded environment. On a high-end system, the extra level of indirection is still a downside. (At least the pointer will typically be in the same cache line as the start of the data and the vtable pointer, so the extra latency is just 1 L1d cache ~load-use latency. Not an extra cache miss)

            – Peter Cordes
            6 hours ago












          • @PeterCordes: Note that the OPs solution grows the size of every object by ??? due to the virtual methods. That's probably 1 pointer, but it's worth noting that the size regression is smaller than it first appears. This also has fewer indirections, since we can directly deference the given pointer, wheras the OP's code has to dereference the virtual method, then call that to get a pointer, then dereference the pointer.

            – Mooing Duck
            3 hours ago












          • @MooingDuck: (successful) Branch prediction / speculation hides the latency of the function pointer, but yes it's a pretty terrible choice if you don't need polymorphism. It's plausible that polymorphism merely for the purpose of code-reuse is worth it if they're very memory-constrained, but likely having callers that statically know the type pass the right args to generic functions is the way to go, maybe by writing simple non-virtual inline helper functions that pass a pointer+size at each call site, or whatever other way you get a C++ compiler to emit machine code that does that.

            – Peter Cordes
            3 hours ago














          14












          14








          14







          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.






          share|improve this answer















          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.







          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited 18 hours ago

























          answered 18 hours ago









          Nikos C.Nikos C.

          38.1k54171




          38.1k54171







          • 1





            This grows the size of every object by 1 pointer + 1 int. If you have many more instances of these objects than you do derived types, the OP's solution uses less total memory (data + code). They mention they're in a memory-constrained embedded environment. On a high-end system, the extra level of indirection is still a downside. (At least the pointer will typically be in the same cache line as the start of the data and the vtable pointer, so the extra latency is just 1 L1d cache ~load-use latency. Not an extra cache miss)

            – Peter Cordes
            6 hours ago












          • @PeterCordes: Note that the OPs solution grows the size of every object by ??? due to the virtual methods. That's probably 1 pointer, but it's worth noting that the size regression is smaller than it first appears. This also has fewer indirections, since we can directly deference the given pointer, wheras the OP's code has to dereference the virtual method, then call that to get a pointer, then dereference the pointer.

            – Mooing Duck
            3 hours ago












          • @MooingDuck: (successful) Branch prediction / speculation hides the latency of the function pointer, but yes it's a pretty terrible choice if you don't need polymorphism. It's plausible that polymorphism merely for the purpose of code-reuse is worth it if they're very memory-constrained, but likely having callers that statically know the type pass the right args to generic functions is the way to go, maybe by writing simple non-virtual inline helper functions that pass a pointer+size at each call site, or whatever other way you get a C++ compiler to emit machine code that does that.

            – Peter Cordes
            3 hours ago













          • 1





            This grows the size of every object by 1 pointer + 1 int. If you have many more instances of these objects than you do derived types, the OP's solution uses less total memory (data + code). They mention they're in a memory-constrained embedded environment. On a high-end system, the extra level of indirection is still a downside. (At least the pointer will typically be in the same cache line as the start of the data and the vtable pointer, so the extra latency is just 1 L1d cache ~load-use latency. Not an extra cache miss)

            – Peter Cordes
            6 hours ago












          • @PeterCordes: Note that the OPs solution grows the size of every object by ??? due to the virtual methods. That's probably 1 pointer, but it's worth noting that the size regression is smaller than it first appears. This also has fewer indirections, since we can directly deference the given pointer, wheras the OP's code has to dereference the virtual method, then call that to get a pointer, then dereference the pointer.

            – Mooing Duck
            3 hours ago












          • @MooingDuck: (successful) Branch prediction / speculation hides the latency of the function pointer, but yes it's a pretty terrible choice if you don't need polymorphism. It's plausible that polymorphism merely for the purpose of code-reuse is worth it if they're very memory-constrained, but likely having callers that statically know the type pass the right args to generic functions is the way to go, maybe by writing simple non-virtual inline helper functions that pass a pointer+size at each call site, or whatever other way you get a C++ compiler to emit machine code that does that.

            – Peter Cordes
            3 hours ago








          1




          1





          This grows the size of every object by 1 pointer + 1 int. If you have many more instances of these objects than you do derived types, the OP's solution uses less total memory (data + code). They mention they're in a memory-constrained embedded environment. On a high-end system, the extra level of indirection is still a downside. (At least the pointer will typically be in the same cache line as the start of the data and the vtable pointer, so the extra latency is just 1 L1d cache ~load-use latency. Not an extra cache miss)

          – Peter Cordes
          6 hours ago






          This grows the size of every object by 1 pointer + 1 int. If you have many more instances of these objects than you do derived types, the OP's solution uses less total memory (data + code). They mention they're in a memory-constrained embedded environment. On a high-end system, the extra level of indirection is still a downside. (At least the pointer will typically be in the same cache line as the start of the data and the vtable pointer, so the extra latency is just 1 L1d cache ~load-use latency. Not an extra cache miss)

          – Peter Cordes
          6 hours ago














          @PeterCordes: Note that the OPs solution grows the size of every object by ??? due to the virtual methods. That's probably 1 pointer, but it's worth noting that the size regression is smaller than it first appears. This also has fewer indirections, since we can directly deference the given pointer, wheras the OP's code has to dereference the virtual method, then call that to get a pointer, then dereference the pointer.

          – Mooing Duck
          3 hours ago






          @PeterCordes: Note that the OPs solution grows the size of every object by ??? due to the virtual methods. That's probably 1 pointer, but it's worth noting that the size regression is smaller than it first appears. This also has fewer indirections, since we can directly deference the given pointer, wheras the OP's code has to dereference the virtual method, then call that to get a pointer, then dereference the pointer.

          – Mooing Duck
          3 hours ago














          @MooingDuck: (successful) Branch prediction / speculation hides the latency of the function pointer, but yes it's a pretty terrible choice if you don't need polymorphism. It's plausible that polymorphism merely for the purpose of code-reuse is worth it if they're very memory-constrained, but likely having callers that statically know the type pass the right args to generic functions is the way to go, maybe by writing simple non-virtual inline helper functions that pass a pointer+size at each call site, or whatever other way you get a C++ compiler to emit machine code that does that.

          – Peter Cordes
          3 hours ago






          @MooingDuck: (successful) Branch prediction / speculation hides the latency of the function pointer, but yes it's a pretty terrible choice if you don't need polymorphism. It's plausible that polymorphism merely for the purpose of code-reuse is worth it if they're very memory-constrained, but likely having callers that statically know the type pass the right args to generic functions is the way to go, maybe by writing simple non-virtual inline helper functions that pass a pointer+size at each call site, or whatever other way you get a C++ compiler to emit machine code that does that.

          – Peter Cordes
          3 hours ago














          6














          You could make Base a template and take the length of your const array. Something like this:



          template<std::size_t Length>
          class Base

          public:
          struct SomeInfo

          const char *name;
          const float value;
          ;

          const SomeInfo c_myInfo[Length];

          void iterateInfo()

          //I would love to just write
          for(const auto& info : c_myInfo)
          // work with info


          ;


          And then initialize the array accordingly from each base class:



          class DerivedA : public Base<2>

          public:
          DerivedA() : Base<2> SomeInfo"NameA1", 1.1f, "NameA2", 1.2f
          ;

          class DerivedB : public Base<3>

          public:
          DerivedB() : Base<3> SomeInfo"NameB1", 2.1f, "NameB2", 2.2f, "NameB2", 2.3f
          ;


          And then use as you normally would. This method removes the polymorphism and uses no heap allocation (e.g. no std::vector), just as user SirNobbyNobbs requested.






          share|improve this answer





























            6














            You could make Base a template and take the length of your const array. Something like this:



            template<std::size_t Length>
            class Base

            public:
            struct SomeInfo

            const char *name;
            const float value;
            ;

            const SomeInfo c_myInfo[Length];

            void iterateInfo()

            //I would love to just write
            for(const auto& info : c_myInfo)
            // work with info


            ;


            And then initialize the array accordingly from each base class:



            class DerivedA : public Base<2>

            public:
            DerivedA() : Base<2> SomeInfo"NameA1", 1.1f, "NameA2", 1.2f
            ;

            class DerivedB : public Base<3>

            public:
            DerivedB() : Base<3> SomeInfo"NameB1", 2.1f, "NameB2", 2.2f, "NameB2", 2.3f
            ;


            And then use as you normally would. This method removes the polymorphism and uses no heap allocation (e.g. no std::vector), just as user SirNobbyNobbs requested.






            share|improve this answer



























              6












              6








              6







              You could make Base a template and take the length of your const array. Something like this:



              template<std::size_t Length>
              class Base

              public:
              struct SomeInfo

              const char *name;
              const float value;
              ;

              const SomeInfo c_myInfo[Length];

              void iterateInfo()

              //I would love to just write
              for(const auto& info : c_myInfo)
              // work with info


              ;


              And then initialize the array accordingly from each base class:



              class DerivedA : public Base<2>

              public:
              DerivedA() : Base<2> SomeInfo"NameA1", 1.1f, "NameA2", 1.2f
              ;

              class DerivedB : public Base<3>

              public:
              DerivedB() : Base<3> SomeInfo"NameB1", 2.1f, "NameB2", 2.2f, "NameB2", 2.3f
              ;


              And then use as you normally would. This method removes the polymorphism and uses no heap allocation (e.g. no std::vector), just as user SirNobbyNobbs requested.






              share|improve this answer















              You could make Base a template and take the length of your const array. Something like this:



              template<std::size_t Length>
              class Base

              public:
              struct SomeInfo

              const char *name;
              const float value;
              ;

              const SomeInfo c_myInfo[Length];

              void iterateInfo()

              //I would love to just write
              for(const auto& info : c_myInfo)
              // work with info


              ;


              And then initialize the array accordingly from each base class:



              class DerivedA : public Base<2>

              public:
              DerivedA() : Base<2> SomeInfo"NameA1", 1.1f, "NameA2", 1.2f
              ;

              class DerivedB : public Base<3>

              public:
              DerivedB() : Base<3> SomeInfo"NameB1", 2.1f, "NameB2", 2.2f, "NameB2", 2.3f
              ;


              And then use as you normally would. This method removes the polymorphism and uses no heap allocation (e.g. no std::vector), just as user SirNobbyNobbs requested.







              share|improve this answer














              share|improve this answer



              share|improve this answer








              edited 3 hours ago









              Peter Mortensen

              14.2k1988114




              14.2k1988114










              answered 18 hours ago









              DeiDeiDeiDei

              6,42653656




              6,42653656





















                  3














                  You can use CRTP:



                  template<class Derived>
                  class impl_getDerivedInfo
                  :public Base


                  virtual const SomeInfo *getDerivedInfo(u8_t &length) override

                  //Duplicated code in every derived implementation....
                  auto& self = static_cast<Derived&>(*this);
                  length = sizeof(self.c_myInfo) / sizeof(self.c_myInfo[0]);
                  return self.c_myInfo;

                  ;


                  class DerivedA : public impl_getDerivedInfo<DerivedA>

                  public:
                  const SomeInfo c_myInfo[2] "NameA1", 1.1f, "NameA2", 1.2f ;
                  ;

                  class DerivedB : public impl_getDerivedInfo<DerivedB>

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

                  ;





                  share|improve this answer























                  • Does getDerivedInfo still need to be virtual here? Also you could turn the duplicated code into just a simple caller for a base-class generic function that takes pointer + length. That would give you clean syntax for a way to get the compiler to pass pointer+length to a common non-duplicated implementation.

                    – Peter Cordes
                    2 hours ago















                  3














                  You can use CRTP:



                  template<class Derived>
                  class impl_getDerivedInfo
                  :public Base


                  virtual const SomeInfo *getDerivedInfo(u8_t &length) override

                  //Duplicated code in every derived implementation....
                  auto& self = static_cast<Derived&>(*this);
                  length = sizeof(self.c_myInfo) / sizeof(self.c_myInfo[0]);
                  return self.c_myInfo;

                  ;


                  class DerivedA : public impl_getDerivedInfo<DerivedA>

                  public:
                  const SomeInfo c_myInfo[2] "NameA1", 1.1f, "NameA2", 1.2f ;
                  ;

                  class DerivedB : public impl_getDerivedInfo<DerivedB>

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

                  ;





                  share|improve this answer























                  • Does getDerivedInfo still need to be virtual here? Also you could turn the duplicated code into just a simple caller for a base-class generic function that takes pointer + length. That would give you clean syntax for a way to get the compiler to pass pointer+length to a common non-duplicated implementation.

                    – Peter Cordes
                    2 hours ago













                  3












                  3








                  3







                  You can use CRTP:



                  template<class Derived>
                  class impl_getDerivedInfo
                  :public Base


                  virtual const SomeInfo *getDerivedInfo(u8_t &length) override

                  //Duplicated code in every derived implementation....
                  auto& self = static_cast<Derived&>(*this);
                  length = sizeof(self.c_myInfo) / sizeof(self.c_myInfo[0]);
                  return self.c_myInfo;

                  ;


                  class DerivedA : public impl_getDerivedInfo<DerivedA>

                  public:
                  const SomeInfo c_myInfo[2] "NameA1", 1.1f, "NameA2", 1.2f ;
                  ;

                  class DerivedB : public impl_getDerivedInfo<DerivedB>

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

                  ;





                  share|improve this answer













                  You can use CRTP:



                  template<class Derived>
                  class impl_getDerivedInfo
                  :public Base


                  virtual const SomeInfo *getDerivedInfo(u8_t &length) override

                  //Duplicated code in every derived implementation....
                  auto& self = static_cast<Derived&>(*this);
                  length = sizeof(self.c_myInfo) / sizeof(self.c_myInfo[0]);
                  return self.c_myInfo;

                  ;


                  class DerivedA : public impl_getDerivedInfo<DerivedA>

                  public:
                  const SomeInfo c_myInfo[2] "NameA1", 1.1f, "NameA2", 1.2f ;
                  ;

                  class DerivedB : public impl_getDerivedInfo<DerivedB>

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

                  ;






                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  answered 18 hours ago









                  OlivOliv

                  11k12058




                  11k12058












                  • Does getDerivedInfo still need to be virtual here? Also you could turn the duplicated code into just a simple caller for a base-class generic function that takes pointer + length. That would give you clean syntax for a way to get the compiler to pass pointer+length to a common non-duplicated implementation.

                    – Peter Cordes
                    2 hours ago

















                  • Does getDerivedInfo still need to be virtual here? Also you could turn the duplicated code into just a simple caller for a base-class generic function that takes pointer + length. That would give you clean syntax for a way to get the compiler to pass pointer+length to a common non-duplicated implementation.

                    – Peter Cordes
                    2 hours ago
















                  Does getDerivedInfo still need to be virtual here? Also you could turn the duplicated code into just a simple caller for a base-class generic function that takes pointer + length. That would give you clean syntax for a way to get the compiler to pass pointer+length to a common non-duplicated implementation.

                  – Peter Cordes
                  2 hours ago





                  Does getDerivedInfo still need to be virtual here? Also you could turn the duplicated code into just a simple caller for a base-class generic function that takes pointer + length. That would give you clean syntax for a way to get the compiler to pass pointer+length to a common non-duplicated implementation.

                  – Peter Cordes
                  2 hours ago











                  2














                  One way with C++17 would be to return a "view" object representing your content list. This can then be used in a C++11 for statement. You could write a base function that converts start+len into a view, so you don't need to add to the virtual method cruft.



                  It is not that difficult to create a view object that is compatible with C++11 for statement. Alternatively, you could consider using the C++98 for_each templates that can take a begin and end iterator: Your start iterator is start; the end iterator is start+len.






                  share|improve this answer





























                    2














                    One way with C++17 would be to return a "view" object representing your content list. This can then be used in a C++11 for statement. You could write a base function that converts start+len into a view, so you don't need to add to the virtual method cruft.



                    It is not that difficult to create a view object that is compatible with C++11 for statement. Alternatively, you could consider using the C++98 for_each templates that can take a begin and end iterator: Your start iterator is start; the end iterator is start+len.






                    share|improve this answer



























                      2












                      2








                      2







                      One way with C++17 would be to return a "view" object representing your content list. This can then be used in a C++11 for statement. You could write a base function that converts start+len into a view, so you don't need to add to the virtual method cruft.



                      It is not that difficult to create a view object that is compatible with C++11 for statement. Alternatively, you could consider using the C++98 for_each templates that can take a begin and end iterator: Your start iterator is start; the end iterator is start+len.






                      share|improve this answer















                      One way with C++17 would be to return a "view" object representing your content list. This can then be used in a C++11 for statement. You could write a base function that converts start+len into a view, so you don't need to add to the virtual method cruft.



                      It is not that difficult to create a view object that is compatible with C++11 for statement. Alternatively, you could consider using the C++98 for_each templates that can take a begin and end iterator: Your start iterator is start; the end iterator is start+len.







                      share|improve this answer














                      share|improve this answer



                      share|improve this answer








                      edited 3 hours ago









                      Peter Mortensen

                      14.2k1988114




                      14.2k1988114










                      answered 16 hours ago









                      Gem TaylorGem Taylor

                      2,905222




                      2,905222





















                          1














                          Okay then let's simplify all the unnecessary complications :)



                          Your code really boils down to the following:



                          SomeInfo.h



                          struct SomeInfo

                          const char *name;
                          const f32_t value;
                          ;

                          void processData(const SomeInfo* c_myInfo, u8_t len);


                          SomeInfo.cpp



                          #include "SomeInfo.h"

                          void processData(const SomeInfo* c_myInfo, u8_t len)

                          for (u8_t i = 0; i < len; i++)

                          DPRINTF("Name: %s - Value: %f n", c_myInfo[i].name, c_myInfo[i].value);




                          data.h



                          #include "SomeInfo.h"

                          struct A

                          const SomeInfo info[2] "NameA1", 1.1f, "NameA2", 1.2f ;
                          static const u8_t len = 2;
                          ;

                          struct B

                          const SomeInfo info[3] "NameB1", 2.1f, "NameB2", 2.2f, "NameB2", 2.3f ;
                          static const u8_t len = 3;
                          ;


                          main.cpp



                          #include "data.h"

                          int
                          main()

                          A a;
                          B b;
                          processData(a.info, A::len);
                          processData(b.info, B::len);






                          share|improve this answer

























                          • Given the provided example you are right.But this is just a simplification to highlight my problem. The base class is already several hundred lines of code and every derived class also has much more inside than just the const info.

                            – SirNobbyNobbs
                            18 hours ago






                          • 5





                            Well, I can imagine. All I can suggest is to use composition instead of inheritance along with simple functions. Coding can be a simple and pleasurable experience. We just complicate everything for some reason :)

                            – Adam Zahran
                            17 hours ago






                          • 2





                            @SirNobbyNobbs "The base class is already several hundred lines of code" - That is more of a code smell than anything that your simplified example might highlight,

                            – Goyo
                            7 hours ago







                          • 3





                            There's no point having a u8_t len = 3; member in each struct; the length of the info[] array member is already statically known as part of the derived type. @SirNobbyNobbs: What you could do is have a small inline wrapper in each of A and B that passes the right args to a common processData function. It can be virtual if you need it to be, but letting it inline into each call site when you have full type info is good. (final on the derived type functions allows that in more cases.)

                            – Peter Cordes
                            6 hours ago











                          • @PeterCordes Well I thought of that. But in order to retrieve the size we would need a function. This function could either be templated or repeated. But since I'm going for simplicity and avoiding code generation here I decided a little extra u8_t in the struct will harm no one :)

                            – Adam Zahran
                            6 hours ago















                          1














                          Okay then let's simplify all the unnecessary complications :)



                          Your code really boils down to the following:



                          SomeInfo.h



                          struct SomeInfo

                          const char *name;
                          const f32_t value;
                          ;

                          void processData(const SomeInfo* c_myInfo, u8_t len);


                          SomeInfo.cpp



                          #include "SomeInfo.h"

                          void processData(const SomeInfo* c_myInfo, u8_t len)

                          for (u8_t i = 0; i < len; i++)

                          DPRINTF("Name: %s - Value: %f n", c_myInfo[i].name, c_myInfo[i].value);




                          data.h



                          #include "SomeInfo.h"

                          struct A

                          const SomeInfo info[2] "NameA1", 1.1f, "NameA2", 1.2f ;
                          static const u8_t len = 2;
                          ;

                          struct B

                          const SomeInfo info[3] "NameB1", 2.1f, "NameB2", 2.2f, "NameB2", 2.3f ;
                          static const u8_t len = 3;
                          ;


                          main.cpp



                          #include "data.h"

                          int
                          main()

                          A a;
                          B b;
                          processData(a.info, A::len);
                          processData(b.info, B::len);






                          share|improve this answer

























                          • Given the provided example you are right.But this is just a simplification to highlight my problem. The base class is already several hundred lines of code and every derived class also has much more inside than just the const info.

                            – SirNobbyNobbs
                            18 hours ago






                          • 5





                            Well, I can imagine. All I can suggest is to use composition instead of inheritance along with simple functions. Coding can be a simple and pleasurable experience. We just complicate everything for some reason :)

                            – Adam Zahran
                            17 hours ago






                          • 2





                            @SirNobbyNobbs "The base class is already several hundred lines of code" - That is more of a code smell than anything that your simplified example might highlight,

                            – Goyo
                            7 hours ago







                          • 3





                            There's no point having a u8_t len = 3; member in each struct; the length of the info[] array member is already statically known as part of the derived type. @SirNobbyNobbs: What you could do is have a small inline wrapper in each of A and B that passes the right args to a common processData function. It can be virtual if you need it to be, but letting it inline into each call site when you have full type info is good. (final on the derived type functions allows that in more cases.)

                            – Peter Cordes
                            6 hours ago











                          • @PeterCordes Well I thought of that. But in order to retrieve the size we would need a function. This function could either be templated or repeated. But since I'm going for simplicity and avoiding code generation here I decided a little extra u8_t in the struct will harm no one :)

                            – Adam Zahran
                            6 hours ago













                          1












                          1








                          1







                          Okay then let's simplify all the unnecessary complications :)



                          Your code really boils down to the following:



                          SomeInfo.h



                          struct SomeInfo

                          const char *name;
                          const f32_t value;
                          ;

                          void processData(const SomeInfo* c_myInfo, u8_t len);


                          SomeInfo.cpp



                          #include "SomeInfo.h"

                          void processData(const SomeInfo* c_myInfo, u8_t len)

                          for (u8_t i = 0; i < len; i++)

                          DPRINTF("Name: %s - Value: %f n", c_myInfo[i].name, c_myInfo[i].value);




                          data.h



                          #include "SomeInfo.h"

                          struct A

                          const SomeInfo info[2] "NameA1", 1.1f, "NameA2", 1.2f ;
                          static const u8_t len = 2;
                          ;

                          struct B

                          const SomeInfo info[3] "NameB1", 2.1f, "NameB2", 2.2f, "NameB2", 2.3f ;
                          static const u8_t len = 3;
                          ;


                          main.cpp



                          #include "data.h"

                          int
                          main()

                          A a;
                          B b;
                          processData(a.info, A::len);
                          processData(b.info, B::len);






                          share|improve this answer















                          Okay then let's simplify all the unnecessary complications :)



                          Your code really boils down to the following:



                          SomeInfo.h



                          struct SomeInfo

                          const char *name;
                          const f32_t value;
                          ;

                          void processData(const SomeInfo* c_myInfo, u8_t len);


                          SomeInfo.cpp



                          #include "SomeInfo.h"

                          void processData(const SomeInfo* c_myInfo, u8_t len)

                          for (u8_t i = 0; i < len; i++)

                          DPRINTF("Name: %s - Value: %f n", c_myInfo[i].name, c_myInfo[i].value);




                          data.h



                          #include "SomeInfo.h"

                          struct A

                          const SomeInfo info[2] "NameA1", 1.1f, "NameA2", 1.2f ;
                          static const u8_t len = 2;
                          ;

                          struct B

                          const SomeInfo info[3] "NameB1", 2.1f, "NameB2", 2.2f, "NameB2", 2.3f ;
                          static const u8_t len = 3;
                          ;


                          main.cpp



                          #include "data.h"

                          int
                          main()

                          A a;
                          B b;
                          processData(a.info, A::len);
                          processData(b.info, B::len);







                          share|improve this answer














                          share|improve this answer



                          share|improve this answer








                          edited 4 hours ago

























                          answered 18 hours ago









                          Adam ZahranAdam Zahran

                          557622




                          557622












                          • Given the provided example you are right.But this is just a simplification to highlight my problem. The base class is already several hundred lines of code and every derived class also has much more inside than just the const info.

                            – SirNobbyNobbs
                            18 hours ago






                          • 5





                            Well, I can imagine. All I can suggest is to use composition instead of inheritance along with simple functions. Coding can be a simple and pleasurable experience. We just complicate everything for some reason :)

                            – Adam Zahran
                            17 hours ago






                          • 2





                            @SirNobbyNobbs "The base class is already several hundred lines of code" - That is more of a code smell than anything that your simplified example might highlight,

                            – Goyo
                            7 hours ago







                          • 3





                            There's no point having a u8_t len = 3; member in each struct; the length of the info[] array member is already statically known as part of the derived type. @SirNobbyNobbs: What you could do is have a small inline wrapper in each of A and B that passes the right args to a common processData function. It can be virtual if you need it to be, but letting it inline into each call site when you have full type info is good. (final on the derived type functions allows that in more cases.)

                            – Peter Cordes
                            6 hours ago











                          • @PeterCordes Well I thought of that. But in order to retrieve the size we would need a function. This function could either be templated or repeated. But since I'm going for simplicity and avoiding code generation here I decided a little extra u8_t in the struct will harm no one :)

                            – Adam Zahran
                            6 hours ago

















                          • Given the provided example you are right.But this is just a simplification to highlight my problem. The base class is already several hundred lines of code and every derived class also has much more inside than just the const info.

                            – SirNobbyNobbs
                            18 hours ago






                          • 5





                            Well, I can imagine. All I can suggest is to use composition instead of inheritance along with simple functions. Coding can be a simple and pleasurable experience. We just complicate everything for some reason :)

                            – Adam Zahran
                            17 hours ago






                          • 2





                            @SirNobbyNobbs "The base class is already several hundred lines of code" - That is more of a code smell than anything that your simplified example might highlight,

                            – Goyo
                            7 hours ago







                          • 3





                            There's no point having a u8_t len = 3; member in each struct; the length of the info[] array member is already statically known as part of the derived type. @SirNobbyNobbs: What you could do is have a small inline wrapper in each of A and B that passes the right args to a common processData function. It can be virtual if you need it to be, but letting it inline into each call site when you have full type info is good. (final on the derived type functions allows that in more cases.)

                            – Peter Cordes
                            6 hours ago











                          • @PeterCordes Well I thought of that. But in order to retrieve the size we would need a function. This function could either be templated or repeated. But since I'm going for simplicity and avoiding code generation here I decided a little extra u8_t in the struct will harm no one :)

                            – Adam Zahran
                            6 hours ago
















                          Given the provided example you are right.But this is just a simplification to highlight my problem. The base class is already several hundred lines of code and every derived class also has much more inside than just the const info.

                          – SirNobbyNobbs
                          18 hours ago





                          Given the provided example you are right.But this is just a simplification to highlight my problem. The base class is already several hundred lines of code and every derived class also has much more inside than just the const info.

                          – SirNobbyNobbs
                          18 hours ago




                          5




                          5





                          Well, I can imagine. All I can suggest is to use composition instead of inheritance along with simple functions. Coding can be a simple and pleasurable experience. We just complicate everything for some reason :)

                          – Adam Zahran
                          17 hours ago





                          Well, I can imagine. All I can suggest is to use composition instead of inheritance along with simple functions. Coding can be a simple and pleasurable experience. We just complicate everything for some reason :)

                          – Adam Zahran
                          17 hours ago




                          2




                          2





                          @SirNobbyNobbs "The base class is already several hundred lines of code" - That is more of a code smell than anything that your simplified example might highlight,

                          – Goyo
                          7 hours ago






                          @SirNobbyNobbs "The base class is already several hundred lines of code" - That is more of a code smell than anything that your simplified example might highlight,

                          – Goyo
                          7 hours ago





                          3




                          3





                          There's no point having a u8_t len = 3; member in each struct; the length of the info[] array member is already statically known as part of the derived type. @SirNobbyNobbs: What you could do is have a small inline wrapper in each of A and B that passes the right args to a common processData function. It can be virtual if you need it to be, but letting it inline into each call site when you have full type info is good. (final on the derived type functions allows that in more cases.)

                          – Peter Cordes
                          6 hours ago





                          There's no point having a u8_t len = 3; member in each struct; the length of the info[] array member is already statically known as part of the derived type. @SirNobbyNobbs: What you could do is have a small inline wrapper in each of A and B that passes the right args to a common processData function. It can be virtual if you need it to be, but letting it inline into each call site when you have full type info is good. (final on the derived type functions allows that in more cases.)

                          – Peter Cordes
                          6 hours ago













                          @PeterCordes Well I thought of that. But in order to retrieve the size we would need a function. This function could either be templated or repeated. But since I'm going for simplicity and avoiding code generation here I decided a little extra u8_t in the struct will harm no one :)

                          – Adam Zahran
                          6 hours ago





                          @PeterCordes Well I thought of that. But in order to retrieve the size we would need a function. This function could either be templated or repeated. But since I'm going for simplicity and avoiding code generation here I decided a little extra u8_t in the struct will harm no one :)

                          – Adam Zahran
                          6 hours ago











                          1














                          You can move your data into a two-dimensional array outside of the classes and have each class return an index which contains relevant data.



                          struct SomeInfo

                          const char *name;
                          const f32_t value;
                          ;

                          const vector<vector<SomeInfo>> masterStore
                          "NameA1", 1.1f, "NameA2", 1.2f,
                          "NameB1", 2.1f, "NameB2", 2.2f, "NameB2", 2.3f
                          ;

                          class Base

                          public:
                          void iterateInfo()

                          // I would love to just write
                          // for(const auto& info : c_myInfo) ...

                          u8_t len = 0;
                          auto index(getIndex());
                          for(const auto& data : masterStore[index])

                          DPRINTF("Name: %s - Value: %f n", data.name, data.value);


                          virtual int getIndex() = 0;
                          ;

                          class DerivedA : public Base

                          public:

                          int getIndex() override

                          return 0;

                          ;

                          class DerivedB : public Base

                          public:

                          int getIndex() override

                          return 1;

                          ;

                          DerivedA instanceA;
                          DerivedB instanceB;
                          instanceA.iterateInfo();
                          instanceB.iterateInfo();





                          share|improve this answer




















                          • 3





                            Why a const std::vector for compile-time-constant data with known fixed size? Seems like a job for a flat 1D std::array<SomeInfo> with each derived class knowing the right start index + offset. (GetIndex returns a std::pair<int,int>). Or a std::array<std::vector<SomeInfo>>. Or maybe implicit lengths by using a flat array of SomeInfo objects with nullptr terminators for the end of each sub-array, if you only ever want to iterate in order.

                            – Peter Cordes
                            6 hours ago















                          1














                          You can move your data into a two-dimensional array outside of the classes and have each class return an index which contains relevant data.



                          struct SomeInfo

                          const char *name;
                          const f32_t value;
                          ;

                          const vector<vector<SomeInfo>> masterStore
                          "NameA1", 1.1f, "NameA2", 1.2f,
                          "NameB1", 2.1f, "NameB2", 2.2f, "NameB2", 2.3f
                          ;

                          class Base

                          public:
                          void iterateInfo()

                          // I would love to just write
                          // for(const auto& info : c_myInfo) ...

                          u8_t len = 0;
                          auto index(getIndex());
                          for(const auto& data : masterStore[index])

                          DPRINTF("Name: %s - Value: %f n", data.name, data.value);


                          virtual int getIndex() = 0;
                          ;

                          class DerivedA : public Base

                          public:

                          int getIndex() override

                          return 0;

                          ;

                          class DerivedB : public Base

                          public:

                          int getIndex() override

                          return 1;

                          ;

                          DerivedA instanceA;
                          DerivedB instanceB;
                          instanceA.iterateInfo();
                          instanceB.iterateInfo();





                          share|improve this answer




















                          • 3





                            Why a const std::vector for compile-time-constant data with known fixed size? Seems like a job for a flat 1D std::array<SomeInfo> with each derived class knowing the right start index + offset. (GetIndex returns a std::pair<int,int>). Or a std::array<std::vector<SomeInfo>>. Or maybe implicit lengths by using a flat array of SomeInfo objects with nullptr terminators for the end of each sub-array, if you only ever want to iterate in order.

                            – Peter Cordes
                            6 hours ago













                          1












                          1








                          1







                          You can move your data into a two-dimensional array outside of the classes and have each class return an index which contains relevant data.



                          struct SomeInfo

                          const char *name;
                          const f32_t value;
                          ;

                          const vector<vector<SomeInfo>> masterStore
                          "NameA1", 1.1f, "NameA2", 1.2f,
                          "NameB1", 2.1f, "NameB2", 2.2f, "NameB2", 2.3f
                          ;

                          class Base

                          public:
                          void iterateInfo()

                          // I would love to just write
                          // for(const auto& info : c_myInfo) ...

                          u8_t len = 0;
                          auto index(getIndex());
                          for(const auto& data : masterStore[index])

                          DPRINTF("Name: %s - Value: %f n", data.name, data.value);


                          virtual int getIndex() = 0;
                          ;

                          class DerivedA : public Base

                          public:

                          int getIndex() override

                          return 0;

                          ;

                          class DerivedB : public Base

                          public:

                          int getIndex() override

                          return 1;

                          ;

                          DerivedA instanceA;
                          DerivedB instanceB;
                          instanceA.iterateInfo();
                          instanceB.iterateInfo();





                          share|improve this answer















                          You can move your data into a two-dimensional array outside of the classes and have each class return an index which contains relevant data.



                          struct SomeInfo

                          const char *name;
                          const f32_t value;
                          ;

                          const vector<vector<SomeInfo>> masterStore
                          "NameA1", 1.1f, "NameA2", 1.2f,
                          "NameB1", 2.1f, "NameB2", 2.2f, "NameB2", 2.3f
                          ;

                          class Base

                          public:
                          void iterateInfo()

                          // I would love to just write
                          // for(const auto& info : c_myInfo) ...

                          u8_t len = 0;
                          auto index(getIndex());
                          for(const auto& data : masterStore[index])

                          DPRINTF("Name: %s - Value: %f n", data.name, data.value);


                          virtual int getIndex() = 0;
                          ;

                          class DerivedA : public Base

                          public:

                          int getIndex() override

                          return 0;

                          ;

                          class DerivedB : public Base

                          public:

                          int getIndex() override

                          return 1;

                          ;

                          DerivedA instanceA;
                          DerivedB instanceB;
                          instanceA.iterateInfo();
                          instanceB.iterateInfo();






                          share|improve this answer














                          share|improve this answer



                          share|improve this answer








                          edited 3 hours ago









                          Peter Mortensen

                          14.2k1988114




                          14.2k1988114










                          answered 18 hours ago









                          Tanveer BadarTanveer Badar

                          1,6861320




                          1,6861320







                          • 3





                            Why a const std::vector for compile-time-constant data with known fixed size? Seems like a job for a flat 1D std::array<SomeInfo> with each derived class knowing the right start index + offset. (GetIndex returns a std::pair<int,int>). Or a std::array<std::vector<SomeInfo>>. Or maybe implicit lengths by using a flat array of SomeInfo objects with nullptr terminators for the end of each sub-array, if you only ever want to iterate in order.

                            – Peter Cordes
                            6 hours ago












                          • 3





                            Why a const std::vector for compile-time-constant data with known fixed size? Seems like a job for a flat 1D std::array<SomeInfo> with each derived class knowing the right start index + offset. (GetIndex returns a std::pair<int,int>). Or a std::array<std::vector<SomeInfo>>. Or maybe implicit lengths by using a flat array of SomeInfo objects with nullptr terminators for the end of each sub-array, if you only ever want to iterate in order.

                            – Peter Cordes
                            6 hours ago







                          3




                          3





                          Why a const std::vector for compile-time-constant data with known fixed size? Seems like a job for a flat 1D std::array<SomeInfo> with each derived class knowing the right start index + offset. (GetIndex returns a std::pair<int,int>). Or a std::array<std::vector<SomeInfo>>. Or maybe implicit lengths by using a flat array of SomeInfo objects with nullptr terminators for the end of each sub-array, if you only ever want to iterate in order.

                          – Peter Cordes
                          6 hours ago





                          Why a const std::vector for compile-time-constant data with known fixed size? Seems like a job for a flat 1D std::array<SomeInfo> with each derived class knowing the right start index + offset. (GetIndex returns a std::pair<int,int>). Or a std::array<std::vector<SomeInfo>>. Or maybe implicit lengths by using a flat array of SomeInfo objects with nullptr terminators for the end of each sub-array, if you only ever want to iterate in order.

                          – Peter Cordes
                          6 hours ago











                          1














                          Just make the virtual function return a reference to the data directly (you need to change to vector then - not possible with array or C style array types with different sizes):



                          virtual const std::vector<SomeInfo>& getDerivedInfo() = 0;


                          or if pointers are the only feasible option, as a pointer range (iterators/range adapter would be preferred though if possible - more on that):



                          virtual std::pair<SomeInfo*, SomeInfo*> getDerivedInfo() = 0;


                          To make this last method work with range-based for loop: one way is to make a small 'range view' type that has the functions begin()/end() - essential a pair with begin()/end()



                          Example:



                          template<class T>
                          struct ptr_range
                          std::pair<T*, T*> range_;
                          auto begin()return range_.begin();
                          auto end()return range_.end();
                          ;


                          Then construct it with:



                          virtual const ptr_range<SomeInfo> getDerivedInfo() override

                          return std::begin(c_myInfo), std::end(c_myInfo);



                          It is easy to make it non-template if a template is not desired.






                          share|improve this answer





























                            1














                            Just make the virtual function return a reference to the data directly (you need to change to vector then - not possible with array or C style array types with different sizes):



                            virtual const std::vector<SomeInfo>& getDerivedInfo() = 0;


                            or if pointers are the only feasible option, as a pointer range (iterators/range adapter would be preferred though if possible - more on that):



                            virtual std::pair<SomeInfo*, SomeInfo*> getDerivedInfo() = 0;


                            To make this last method work with range-based for loop: one way is to make a small 'range view' type that has the functions begin()/end() - essential a pair with begin()/end()



                            Example:



                            template<class T>
                            struct ptr_range
                            std::pair<T*, T*> range_;
                            auto begin()return range_.begin();
                            auto end()return range_.end();
                            ;


                            Then construct it with:



                            virtual const ptr_range<SomeInfo> getDerivedInfo() override

                            return std::begin(c_myInfo), std::end(c_myInfo);



                            It is easy to make it non-template if a template is not desired.






                            share|improve this answer



























                              1












                              1








                              1







                              Just make the virtual function return a reference to the data directly (you need to change to vector then - not possible with array or C style array types with different sizes):



                              virtual const std::vector<SomeInfo>& getDerivedInfo() = 0;


                              or if pointers are the only feasible option, as a pointer range (iterators/range adapter would be preferred though if possible - more on that):



                              virtual std::pair<SomeInfo*, SomeInfo*> getDerivedInfo() = 0;


                              To make this last method work with range-based for loop: one way is to make a small 'range view' type that has the functions begin()/end() - essential a pair with begin()/end()



                              Example:



                              template<class T>
                              struct ptr_range
                              std::pair<T*, T*> range_;
                              auto begin()return range_.begin();
                              auto end()return range_.end();
                              ;


                              Then construct it with:



                              virtual const ptr_range<SomeInfo> getDerivedInfo() override

                              return std::begin(c_myInfo), std::end(c_myInfo);



                              It is easy to make it non-template if a template is not desired.






                              share|improve this answer















                              Just make the virtual function return a reference to the data directly (you need to change to vector then - not possible with array or C style array types with different sizes):



                              virtual const std::vector<SomeInfo>& getDerivedInfo() = 0;


                              or if pointers are the only feasible option, as a pointer range (iterators/range adapter would be preferred though if possible - more on that):



                              virtual std::pair<SomeInfo*, SomeInfo*> getDerivedInfo() = 0;


                              To make this last method work with range-based for loop: one way is to make a small 'range view' type that has the functions begin()/end() - essential a pair with begin()/end()



                              Example:



                              template<class T>
                              struct ptr_range
                              std::pair<T*, T*> range_;
                              auto begin()return range_.begin();
                              auto end()return range_.end();
                              ;


                              Then construct it with:



                              virtual const ptr_range<SomeInfo> getDerivedInfo() override

                              return std::begin(c_myInfo), std::end(c_myInfo);



                              It is easy to make it non-template if a template is not desired.







                              share|improve this answer














                              share|improve this answer



                              share|improve this answer








                              edited 3 hours ago









                              Peter Mortensen

                              14.2k1988114




                              14.2k1988114










                              answered 16 hours ago









                              darunedarune

                              2,535826




                              2,535826





















                                  1














                                  Start with a vocabulary type:



                                  template<class T>
                                  struct span
                                  T* b = nullptr;
                                  T* e = nullptr;
                                  span( T* s, T* f ):b(s), e(f)
                                  span( T* s, size_t l ):span(s, s+l)
                                  template<size_t N>
                                  span( T(&arr)[N] ):span(arr, N)
                                  T* begin() const return b;
                                  T* end() const return e;
                                  size_t size() const return end()-begin();
                                  bool empty() const return size()==0;
                                  T& front() const return *begin();
                                  T& back() const return *(end()-1);
                                  ;

                                  // This is just here for the other array ctor:
                                  template<class T>
                                  struct span<T const>
                                  T const* b = nullptr;
                                  T const* e = nullptr;
                                  span( T const* s, T const* f ):b(s), e(f)
                                  span( T const* s, size_t l ):span(s, s+l)
                                  template<size_t N>
                                  span( T const(&arr)[N] ):span(arr, N)
                                  template<size_t N>
                                  span( T(&arr)[N] ):span(arr, N)
                                  T const* begin() const return b;
                                  T const* end() const return e;
                                  size_t size() const return end()-begin();
                                  bool empty() const return size()==0;
                                  T const& front() const return *begin();
                                  T const& back() const return *(end()-1);
                                  ;


                                  Now we can talk about a span<char>:



                                  class Base

                                  public:
                                  void iterateInfo()

                                  for(const auto& info : c_mySpan)
                                  DPRINTF("Name: %s - Value: %f n", info.name, info.value);


                                  private:
                                  span<const char> c_mySpan;
                                  Base( span<const char> s ):c_mySpan(s)
                                  Base(Base const&)=delete; // probably unsafe
                                  ;


                                  Now your derived looks like:



                                  class DerivedA : public Base

                                  public:
                                  const SomeInfo c_myInfo[2] "NameA1", 1.1f, "NameA2", 1.2f ;
                                  DerivedA() : Base(c_myInfo)
                                  ;


                                  This has overhead of two pointers per Base. A vtable uses one pointer, makes your type abstract, adds indirection, and adds one global vtable per Derived type.



                                  Now, in theory, you could get the overhead of this down to the length of the array, and presume that the array data starts right after Base, but that is fragile, non-portable and only useful if desperate.



                                  While you may be rightly leery of templates in embedded code (as you should be of any kind of code generation; code generation means you can generate more than O(1) binary from O(1) code). The span vocabulary type is compact and should be inlined to nothing if your compiler settings are reasonably aggressive.






                                  share|improve this answer





























                                    1














                                    Start with a vocabulary type:



                                    template<class T>
                                    struct span
                                    T* b = nullptr;
                                    T* e = nullptr;
                                    span( T* s, T* f ):b(s), e(f)
                                    span( T* s, size_t l ):span(s, s+l)
                                    template<size_t N>
                                    span( T(&arr)[N] ):span(arr, N)
                                    T* begin() const return b;
                                    T* end() const return e;
                                    size_t size() const return end()-begin();
                                    bool empty() const return size()==0;
                                    T& front() const return *begin();
                                    T& back() const return *(end()-1);
                                    ;

                                    // This is just here for the other array ctor:
                                    template<class T>
                                    struct span<T const>
                                    T const* b = nullptr;
                                    T const* e = nullptr;
                                    span( T const* s, T const* f ):b(s), e(f)
                                    span( T const* s, size_t l ):span(s, s+l)
                                    template<size_t N>
                                    span( T const(&arr)[N] ):span(arr, N)
                                    template<size_t N>
                                    span( T(&arr)[N] ):span(arr, N)
                                    T const* begin() const return b;
                                    T const* end() const return e;
                                    size_t size() const return end()-begin();
                                    bool empty() const return size()==0;
                                    T const& front() const return *begin();
                                    T const& back() const return *(end()-1);
                                    ;


                                    Now we can talk about a span<char>:



                                    class Base

                                    public:
                                    void iterateInfo()

                                    for(const auto& info : c_mySpan)
                                    DPRINTF("Name: %s - Value: %f n", info.name, info.value);


                                    private:
                                    span<const char> c_mySpan;
                                    Base( span<const char> s ):c_mySpan(s)
                                    Base(Base const&)=delete; // probably unsafe
                                    ;


                                    Now your derived looks like:



                                    class DerivedA : public Base

                                    public:
                                    const SomeInfo c_myInfo[2] "NameA1", 1.1f, "NameA2", 1.2f ;
                                    DerivedA() : Base(c_myInfo)
                                    ;


                                    This has overhead of two pointers per Base. A vtable uses one pointer, makes your type abstract, adds indirection, and adds one global vtable per Derived type.



                                    Now, in theory, you could get the overhead of this down to the length of the array, and presume that the array data starts right after Base, but that is fragile, non-portable and only useful if desperate.



                                    While you may be rightly leery of templates in embedded code (as you should be of any kind of code generation; code generation means you can generate more than O(1) binary from O(1) code). The span vocabulary type is compact and should be inlined to nothing if your compiler settings are reasonably aggressive.






                                    share|improve this answer



























                                      1












                                      1








                                      1







                                      Start with a vocabulary type:



                                      template<class T>
                                      struct span
                                      T* b = nullptr;
                                      T* e = nullptr;
                                      span( T* s, T* f ):b(s), e(f)
                                      span( T* s, size_t l ):span(s, s+l)
                                      template<size_t N>
                                      span( T(&arr)[N] ):span(arr, N)
                                      T* begin() const return b;
                                      T* end() const return e;
                                      size_t size() const return end()-begin();
                                      bool empty() const return size()==0;
                                      T& front() const return *begin();
                                      T& back() const return *(end()-1);
                                      ;

                                      // This is just here for the other array ctor:
                                      template<class T>
                                      struct span<T const>
                                      T const* b = nullptr;
                                      T const* e = nullptr;
                                      span( T const* s, T const* f ):b(s), e(f)
                                      span( T const* s, size_t l ):span(s, s+l)
                                      template<size_t N>
                                      span( T const(&arr)[N] ):span(arr, N)
                                      template<size_t N>
                                      span( T(&arr)[N] ):span(arr, N)
                                      T const* begin() const return b;
                                      T const* end() const return e;
                                      size_t size() const return end()-begin();
                                      bool empty() const return size()==0;
                                      T const& front() const return *begin();
                                      T const& back() const return *(end()-1);
                                      ;


                                      Now we can talk about a span<char>:



                                      class Base

                                      public:
                                      void iterateInfo()

                                      for(const auto& info : c_mySpan)
                                      DPRINTF("Name: %s - Value: %f n", info.name, info.value);


                                      private:
                                      span<const char> c_mySpan;
                                      Base( span<const char> s ):c_mySpan(s)
                                      Base(Base const&)=delete; // probably unsafe
                                      ;


                                      Now your derived looks like:



                                      class DerivedA : public Base

                                      public:
                                      const SomeInfo c_myInfo[2] "NameA1", 1.1f, "NameA2", 1.2f ;
                                      DerivedA() : Base(c_myInfo)
                                      ;


                                      This has overhead of two pointers per Base. A vtable uses one pointer, makes your type abstract, adds indirection, and adds one global vtable per Derived type.



                                      Now, in theory, you could get the overhead of this down to the length of the array, and presume that the array data starts right after Base, but that is fragile, non-portable and only useful if desperate.



                                      While you may be rightly leery of templates in embedded code (as you should be of any kind of code generation; code generation means you can generate more than O(1) binary from O(1) code). The span vocabulary type is compact and should be inlined to nothing if your compiler settings are reasonably aggressive.






                                      share|improve this answer















                                      Start with a vocabulary type:



                                      template<class T>
                                      struct span
                                      T* b = nullptr;
                                      T* e = nullptr;
                                      span( T* s, T* f ):b(s), e(f)
                                      span( T* s, size_t l ):span(s, s+l)
                                      template<size_t N>
                                      span( T(&arr)[N] ):span(arr, N)
                                      T* begin() const return b;
                                      T* end() const return e;
                                      size_t size() const return end()-begin();
                                      bool empty() const return size()==0;
                                      T& front() const return *begin();
                                      T& back() const return *(end()-1);
                                      ;

                                      // This is just here for the other array ctor:
                                      template<class T>
                                      struct span<T const>
                                      T const* b = nullptr;
                                      T const* e = nullptr;
                                      span( T const* s, T const* f ):b(s), e(f)
                                      span( T const* s, size_t l ):span(s, s+l)
                                      template<size_t N>
                                      span( T const(&arr)[N] ):span(arr, N)
                                      template<size_t N>
                                      span( T(&arr)[N] ):span(arr, N)
                                      T const* begin() const return b;
                                      T const* end() const return e;
                                      size_t size() const return end()-begin();
                                      bool empty() const return size()==0;
                                      T const& front() const return *begin();
                                      T const& back() const return *(end()-1);
                                      ;


                                      Now we can talk about a span<char>:



                                      class Base

                                      public:
                                      void iterateInfo()

                                      for(const auto& info : c_mySpan)
                                      DPRINTF("Name: %s - Value: %f n", info.name, info.value);


                                      private:
                                      span<const char> c_mySpan;
                                      Base( span<const char> s ):c_mySpan(s)
                                      Base(Base const&)=delete; // probably unsafe
                                      ;


                                      Now your derived looks like:



                                      class DerivedA : public Base

                                      public:
                                      const SomeInfo c_myInfo[2] "NameA1", 1.1f, "NameA2", 1.2f ;
                                      DerivedA() : Base(c_myInfo)
                                      ;


                                      This has overhead of two pointers per Base. A vtable uses one pointer, makes your type abstract, adds indirection, and adds one global vtable per Derived type.



                                      Now, in theory, you could get the overhead of this down to the length of the array, and presume that the array data starts right after Base, but that is fragile, non-portable and only useful if desperate.



                                      While you may be rightly leery of templates in embedded code (as you should be of any kind of code generation; code generation means you can generate more than O(1) binary from O(1) code). The span vocabulary type is compact and should be inlined to nothing if your compiler settings are reasonably aggressive.







                                      share|improve this answer














                                      share|improve this answer



                                      share|improve this answer








                                      edited 3 hours ago









                                      Peter Mortensen

                                      14.2k1988114




                                      14.2k1988114










                                      answered 8 hours ago









                                      Yakk - Adam NevraumontYakk - Adam Nevraumont

                                      193k21204399




                                      193k21204399




















                                          SirNobbyNobbs is a new contributor. Be nice, and check out our Code of Conduct.









                                          draft saved

                                          draft discarded


















                                          SirNobbyNobbs is a new contributor. Be nice, and check out our Code of Conduct.












                                          SirNobbyNobbs is a new contributor. Be nice, and check out our Code of Conduct.











                                          SirNobbyNobbs is a new contributor. Be nice, and check out our Code of Conduct.














                                          Thanks for contributing an answer to Stack Overflow!


                                          • Please be sure to answer the question. Provide details and share your research!

                                          But avoid


                                          • Asking for help, clarification, or responding to other answers.

                                          • Making statements based on opinion; back them up with references or personal experience.

                                          To learn more, see our tips on writing great answers.




                                          draft saved


                                          draft discarded














                                          StackExchange.ready(
                                          function ()
                                          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f56627438%2fhow-do-i-remove-this-inheritance-related-code-smell%23new-answer', 'question_page');

                                          );

                                          Post as a guest















                                          Required, but never shown





















































                                          Required, but never shown














                                          Required, but never shown












                                          Required, but never shown







                                          Required, but never shown

































                                          Required, but never shown














                                          Required, but never shown












                                          Required, but never shown







                                          Required, but never shown







                                          Popular posts from this blog

                                          ParseJSON using SSJSUsing AMPscript with SSJS ActivitiesHow to resubscribe a user in Marketing cloud using SSJS?Pulling Subscriber Status from Lists using SSJSRetrieving Emails using SSJSProblem in updating DE using SSJSUsing SSJS to send single email in Marketing CloudError adding EmailSendDefinition using SSJS

                                          Кампала Садржај Географија Географија Историја Становништво Привреда Партнерски градови Референце Спољашње везе Мени за навигацију0°11′ СГШ; 32°20′ ИГД / 0.18° СГШ; 32.34° ИГД / 0.18; 32.340°11′ СГШ; 32°20′ ИГД / 0.18° СГШ; 32.34° ИГД / 0.18; 32.34МедијиПодациЗванични веб-сајту

                                          19. јануар Садржај Догађаји Рођења Смрти Празници и дани сећања Види још Референце Мени за навигацијуу