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 the best delay to use between characters sent to the serial port

How can I check type T is among parameter pack Ts... in C++?

Why is a blank required between "[[" and "-e xxx" in ksh?

MH370 blackbox - is it still possible to retrieve data from it?

Why cruise at 7000' in an A319?

Pronunciation of "œuf" in "deux œufs kinder" and "bœuf "in "deux bœufs bourguignons" as an exception to silent /f/ in the plural

Signing using digital signatures?

Intuitively, why does putting capacitors in series decrease the equivalent capacitance?

“Transitive verb” + interrupter+ “object”?

What's the point of DHS warning passengers about Manila airport?

Averting Real Women Don’t Wear Dresses

Can you get infinite turns with this 2 card combo?

Should I include salary information on my CV?

Is there any set of 2-6 notes that doesn't have a chord name?

A player is constantly pestering me about rules, what do I do as a DM?

Would adding an external lens allow one area outside the focal plane to be in focus?

Should I tell my insurance company I have an unsecured loan for my new car?

Forgot chonantanu after already making havdalah over wine

If a high rpm motor is run at lower rpm, will it produce more torque?

Three column layout

Did Chinese school textbook maps (c. 1951) "depict China as stretching even into the central Asian republics"?

One folder two different locations on ubuntu 18.04

Short story with brother-sister conjoined twins as protagonists?

How hard is it to sell a home which is currently mortgaged?



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 margin-bottom:0;








26















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 the heap or fancy libraries like Boost is 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.



















  • No, I only instanciate the derived classes.

    – SirNobbyNobbs
    Jun 17 at 8:47











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

    – Tanveer Badar
    Jun 17 at 8:47






  • 4





    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
    Jun 17 at 20:54






  • 2





    Thanks for all the suggestions! So far, Nikos C. answer best suits my needs although Peter Cordes approach is also neat and simple. Just some clarifications: 1) Several users suggested to make c_myInfo static const and they are correct of course. 2) My embedded environment isn´t so small that I have to count every bit and byte. I just don´t want to compile some extra 10 kB on libraries if it can be avoided. Readability is more important than code efficiency.

    – SirNobbyNobbs
    Jun 19 at 6:49


















26















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 the heap or fancy libraries like Boost is 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.



















  • No, I only instanciate the derived classes.

    – SirNobbyNobbs
    Jun 17 at 8:47











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

    – Tanveer Badar
    Jun 17 at 8:47






  • 4





    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
    Jun 17 at 20:54






  • 2





    Thanks for all the suggestions! So far, Nikos C. answer best suits my needs although Peter Cordes approach is also neat and simple. Just some clarifications: 1) Several users suggested to make c_myInfo static const and they are correct of course. 2) My embedded environment isn´t so small that I have to count every bit and byte. I just don´t want to compile some extra 10 kB on libraries if it can be avoided. Readability is more important than code efficiency.

    – SirNobbyNobbs
    Jun 19 at 6:49














26












26








26


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 the heap or fancy libraries like Boost is 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 the heap or fancy libraries like Boost is 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 Jun 19 at 12:35









Masoud Darvishian

1,3554 gold badges18 silver badges30 bronze badges




1,3554 gold badges18 silver badges30 bronze badges






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 Jun 17 at 8:23









SirNobbyNobbsSirNobbyNobbs

1362 silver badges7 bronze badges




1362 silver badges7 bronze badges




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.














  • No, I only instanciate the derived classes.

    – SirNobbyNobbs
    Jun 17 at 8:47











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

    – Tanveer Badar
    Jun 17 at 8:47






  • 4





    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
    Jun 17 at 20:54






  • 2





    Thanks for all the suggestions! So far, Nikos C. answer best suits my needs although Peter Cordes approach is also neat and simple. Just some clarifications: 1) Several users suggested to make c_myInfo static const and they are correct of course. 2) My embedded environment isn´t so small that I have to count every bit and byte. I just don´t want to compile some extra 10 kB on libraries if it can be avoided. Readability is more important than code efficiency.

    – SirNobbyNobbs
    Jun 19 at 6:49


















  • No, I only instanciate the derived classes.

    – SirNobbyNobbs
    Jun 17 at 8:47











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

    – Tanveer Badar
    Jun 17 at 8:47






  • 4





    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
    Jun 17 at 20:54






  • 2





    Thanks for all the suggestions! So far, Nikos C. answer best suits my needs although Peter Cordes approach is also neat and simple. Just some clarifications: 1) Several users suggested to make c_myInfo static const and they are correct of course. 2) My embedded environment isn´t so small that I have to count every bit and byte. I just don´t want to compile some extra 10 kB on libraries if it can be avoided. Readability is more important than code efficiency.

    – SirNobbyNobbs
    Jun 19 at 6:49

















No, I only instanciate the derived classes.

– SirNobbyNobbs
Jun 17 at 8:47





No, I only instanciate the derived classes.

– SirNobbyNobbs
Jun 17 at 8:47













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

– Tanveer Badar
Jun 17 at 8:47





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

– Tanveer Badar
Jun 17 at 8:47




4




4





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
Jun 17 at 20:54





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
Jun 17 at 20:54




2




2





Thanks for all the suggestions! So far, Nikos C. answer best suits my needs although Peter Cordes approach is also neat and simple. Just some clarifications: 1) Several users suggested to make c_myInfo static const and they are correct of course. 2) My embedded environment isn´t so small that I have to count every bit and byte. I just don´t want to compile some extra 10 kB on libraries if it can be avoided. Readability is more important than code efficiency.

– SirNobbyNobbs
Jun 19 at 6:49






Thanks for all the suggestions! So far, Nikos C. answer best suits my needs although Peter Cordes approach is also neat and simple. Just some clarifications: 1) Several users suggested to make c_myInfo static const and they are correct of course. 2) My embedded environment isn´t so small that I have to count every bit and byte. I just don´t want to compile some extra 10 kB on libraries if it can be avoided. Readability is more important than code efficiency.

– SirNobbyNobbs
Jun 19 at 6:49













9 Answers
9






active

oldest

votes


















24














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



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



class Base

public:
struct SomeInfo

const char *name;
const f32_t value;
;

void iterateInfo()

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



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


private:
const SomeInfo* c_info;
int c_info_len;
;

class DerivedA : public Base

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


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

class DerivedB : public Base

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


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


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



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



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






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
    Jun 17 at 20:46







  • 1





    @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
    Jun 18 at 0:31












  • @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
    Jun 18 at 0:37












  • @PeterCordes That's a good point. I updated the answer.

    – Nikos C.
    Jun 18 at 5:07











  • @PeterCordes: "small embedded environments" usually do not feature performance enhancing logic such as "Branch prediction / speculation" because it would add significantly to the transistor count (affecting cost, size, and power consumption)

    – Ben Voigt
    Jun 18 at 5:28


















10














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




















  • 1





    To save code size, make iterateInfo a non-member function that takes a pointer + Length as function args, and give the derived classes simple inline functions that do iterateInfo(c_myInfo, Length); So the loop has one shared implementation and callers just pass it args. Unfortunately I don't think putting it in the base class would get most compilers to only make one definition, unless they use identical code folding optimizations to merge identical function definitions.

    – Peter Cordes
    Jun 18 at 5:56


















6














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
    Jun 17 at 9:38






  • 6





    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
    Jun 17 at 9:42






  • 3





    @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
    Jun 17 at 19:43







  • 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
    Jun 17 at 21:08






  • 1





    If you want to require callers to manually inline, instead of writing a wrapper function, then make it static const int len; The u8 will probably end up still costing you 4 bytes, because it's inside a struct that will get 4-byte alignment (assuming alignof(float) = 4. So the struct size will be a multiple of 4 bytes, because compilers always pad structs to a multiple of their alignof(), so in an array all the elements have correct alignment.

    – Peter Cordes
    Jun 17 at 21:51


















5














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
    Jun 18 at 0:41






  • 1





    @PeterCordes I suppose getDerivedInfo must be virtual because this is the assumption done in the question (Objects are accessed though expression of type Base). The generic function would be so short that it will be inlined whatsoever, resulting in the same code (the expression that initialize length is a constant expression). Virtualization could be implemented by hand, like in the NikosC. answer, in a memory efficient way, but the compiler will not any more be able to perform devirtualization.

    – Oliv
    Jun 18 at 5:52











  • @PeterCordes What is laking to the language is a way to express that a specific non-static data member value in a base class is the same for all object that have the same dynamic type.

    – Oliv
    Jun 18 at 5:52


















4














Start with a vocabulary type:



template<class T>
struct span
T* b = nullptr;
T* e = nullptr;

// these all do something reasonable:
span()=default;
span(span const&)=default;
span& operator=(span const&)=default;

// pair of pointers, or pointer and length:
span( T* s, T* f ):b(s), e(f)
span( T* s, size_t l ):span(s, s+l)

// construct from an array of known length:
template<size_t N>
span( T(&arr)[N] ):span(arr, N)

// Pointers are iterators:
T* begin() const return b;
T* end() const return e;

// extended container-like utility functions:
T* data() const return begin();
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,
// a span of const int can be constructed from
// an array of non-const int.
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);
;


this type has been introduced to C++ std (with slight differences) via the GSL. The basic vocabulary type above is enough if you don't already have it.



A span represents a "pointer" to a block of contiguous objects of known length.



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
































    3














    So if you really want to keep your data organised the way it is, and I can see why you would in real life:



    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
































      3














      How about CRTP + std::array? No extra variables, v-ptr or virtual function calls. std::array is a very thin wrapper around C style array. Empty base class optimization ensures no space is wasted. It looks "elegant" enough to me :)



      template<typename Derived>
      class BaseT

      public:
      struct SomeInfo

      const char *name;
      const f32_t value;
      ;

      void iterateInfo()

      Derived* pDerived = static_cast<Derived*>(this);
      for (const auto& i: pDerived->c_myInfo)

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


      ;

      class DerivedA : public BaseT<DerivedA>

      public:
      const std::array<SomeInfo,2> c_myInfo "NameA1", 1.1f, "NameA2", 1.2f ;
      ;

      class DerivedB : public BaseT<DerivedB>

      public:
      const std::array<SomeInfo, 3> c_myInfo "NameB1", 2.1f, "NameB2", 2.2f, "NameB2", 2.3f ;
      ;





      share|improve this answer










      New contributor



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


























        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
          Jun 17 at 21:00











        • OP didn't share full code so it is a coin toss either way about whether the data is compile time constant or not.

          – Tanveer Badar
          Jun 18 at 8:42











        • Ok, but there's definitely zero point using vector<vector<T>> over array<vector<T>>. The outer array is definitely fixed-size because it has 1 entry per derived type.

          – Peter Cordes
          Jun 18 at 15:49


















        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 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



























          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

























          9 Answers
          9






          active

          oldest

          votes








          9 Answers
          9






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes









          24














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



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



          class Base

          public:
          struct SomeInfo

          const char *name;
          const f32_t value;
          ;

          void iterateInfo()

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



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


          private:
          const SomeInfo* c_info;
          int c_info_len;
          ;

          class DerivedA : public Base

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


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

          class DerivedB : public Base

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


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


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



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



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






          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
            Jun 17 at 20:46







          • 1





            @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
            Jun 18 at 0:31












          • @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
            Jun 18 at 0:37












          • @PeterCordes That's a good point. I updated the answer.

            – Nikos C.
            Jun 18 at 5:07











          • @PeterCordes: "small embedded environments" usually do not feature performance enhancing logic such as "Branch prediction / speculation" because it would add significantly to the transistor count (affecting cost, size, and power consumption)

            – Ben Voigt
            Jun 18 at 5:28















          24














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



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



          class Base

          public:
          struct SomeInfo

          const char *name;
          const f32_t value;
          ;

          void iterateInfo()

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



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


          private:
          const SomeInfo* c_info;
          int c_info_len;
          ;

          class DerivedA : public Base

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


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

          class DerivedB : public Base

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


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


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



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



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






          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
            Jun 17 at 20:46







          • 1





            @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
            Jun 18 at 0:31












          • @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
            Jun 18 at 0:37












          • @PeterCordes That's a good point. I updated the answer.

            – Nikos C.
            Jun 18 at 5:07











          • @PeterCordes: "small embedded environments" usually do not feature performance enhancing logic such as "Branch prediction / speculation" because it would add significantly to the transistor count (affecting cost, size, and power consumption)

            – Ben Voigt
            Jun 18 at 5:28













          24












          24








          24







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



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



          class Base

          public:
          struct SomeInfo

          const char *name;
          const f32_t value;
          ;

          void iterateInfo()

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



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


          private:
          const SomeInfo* c_info;
          int c_info_len;
          ;

          class DerivedA : public Base

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


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

          class DerivedB : public Base

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


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


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



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



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






          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.



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



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







          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited Jun 18 at 6:24

























          answered Jun 17 at 8:51









          Nikos C.Nikos C.

          39.1k6 gold badges45 silver badges75 bronze badges




          39.1k6 gold badges45 silver badges75 bronze badges







          • 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
            Jun 17 at 20:46







          • 1





            @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
            Jun 18 at 0:31












          • @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
            Jun 18 at 0:37












          • @PeterCordes That's a good point. I updated the answer.

            – Nikos C.
            Jun 18 at 5:07











          • @PeterCordes: "small embedded environments" usually do not feature performance enhancing logic such as "Branch prediction / speculation" because it would add significantly to the transistor count (affecting cost, size, and power consumption)

            – Ben Voigt
            Jun 18 at 5:28












          • 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
            Jun 17 at 20:46







          • 1





            @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
            Jun 18 at 0:31












          • @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
            Jun 18 at 0:37












          • @PeterCordes That's a good point. I updated the answer.

            – Nikos C.
            Jun 18 at 5:07











          • @PeterCordes: "small embedded environments" usually do not feature performance enhancing logic such as "Branch prediction / speculation" because it would add significantly to the transistor count (affecting cost, size, and power consumption)

            – Ben Voigt
            Jun 18 at 5:28







          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
          Jun 17 at 20:46






          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
          Jun 17 at 20:46





          1




          1





          @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
          Jun 18 at 0:31






          @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
          Jun 18 at 0:31














          @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
          Jun 18 at 0:37






          @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
          Jun 18 at 0:37














          @PeterCordes That's a good point. I updated the answer.

          – Nikos C.
          Jun 18 at 5:07





          @PeterCordes That's a good point. I updated the answer.

          – Nikos C.
          Jun 18 at 5:07













          @PeterCordes: "small embedded environments" usually do not feature performance enhancing logic such as "Branch prediction / speculation" because it would add significantly to the transistor count (affecting cost, size, and power consumption)

          – Ben Voigt
          Jun 18 at 5:28





          @PeterCordes: "small embedded environments" usually do not feature performance enhancing logic such as "Branch prediction / speculation" because it would add significantly to the transistor count (affecting cost, size, and power consumption)

          – Ben Voigt
          Jun 18 at 5:28













          10














          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




















          • 1





            To save code size, make iterateInfo a non-member function that takes a pointer + Length as function args, and give the derived classes simple inline functions that do iterateInfo(c_myInfo, Length); So the loop has one shared implementation and callers just pass it args. Unfortunately I don't think putting it in the base class would get most compilers to only make one definition, unless they use identical code folding optimizations to merge identical function definitions.

            – Peter Cordes
            Jun 18 at 5:56















          10














          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




















          • 1





            To save code size, make iterateInfo a non-member function that takes a pointer + Length as function args, and give the derived classes simple inline functions that do iterateInfo(c_myInfo, Length); So the loop has one shared implementation and callers just pass it args. Unfortunately I don't think putting it in the base class would get most compilers to only make one definition, unless they use identical code folding optimizations to merge identical function definitions.

            – Peter Cordes
            Jun 18 at 5:56













          10












          10








          10







          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 Jun 17 at 23:58









          Peter Mortensen

          14.2k19 gold badges88 silver badges114 bronze badges




          14.2k19 gold badges88 silver badges114 bronze badges










          answered Jun 17 at 8:44









          DeiDeiDeiDei

          6,5015 gold badges36 silver badges57 bronze badges




          6,5015 gold badges36 silver badges57 bronze badges







          • 1





            To save code size, make iterateInfo a non-member function that takes a pointer + Length as function args, and give the derived classes simple inline functions that do iterateInfo(c_myInfo, Length); So the loop has one shared implementation and callers just pass it args. Unfortunately I don't think putting it in the base class would get most compilers to only make one definition, unless they use identical code folding optimizations to merge identical function definitions.

            – Peter Cordes
            Jun 18 at 5:56












          • 1





            To save code size, make iterateInfo a non-member function that takes a pointer + Length as function args, and give the derived classes simple inline functions that do iterateInfo(c_myInfo, Length); So the loop has one shared implementation and callers just pass it args. Unfortunately I don't think putting it in the base class would get most compilers to only make one definition, unless they use identical code folding optimizations to merge identical function definitions.

            – Peter Cordes
            Jun 18 at 5:56







          1




          1





          To save code size, make iterateInfo a non-member function that takes a pointer + Length as function args, and give the derived classes simple inline functions that do iterateInfo(c_myInfo, Length); So the loop has one shared implementation and callers just pass it args. Unfortunately I don't think putting it in the base class would get most compilers to only make one definition, unless they use identical code folding optimizations to merge identical function definitions.

          – Peter Cordes
          Jun 18 at 5:56





          To save code size, make iterateInfo a non-member function that takes a pointer + Length as function args, and give the derived classes simple inline functions that do iterateInfo(c_myInfo, Length); So the loop has one shared implementation and callers just pass it args. Unfortunately I don't think putting it in the base class would get most compilers to only make one definition, unless they use identical code folding optimizations to merge identical function definitions.

          – Peter Cordes
          Jun 18 at 5:56











          6














          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
            Jun 17 at 9:38






          • 6





            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
            Jun 17 at 9:42






          • 3





            @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
            Jun 17 at 19:43







          • 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
            Jun 17 at 21:08






          • 1





            If you want to require callers to manually inline, instead of writing a wrapper function, then make it static const int len; The u8 will probably end up still costing you 4 bytes, because it's inside a struct that will get 4-byte alignment (assuming alignof(float) = 4. So the struct size will be a multiple of 4 bytes, because compilers always pad structs to a multiple of their alignof(), so in an array all the elements have correct alignment.

            – Peter Cordes
            Jun 17 at 21:51















          6














          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
            Jun 17 at 9:38






          • 6





            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
            Jun 17 at 9:42






          • 3





            @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
            Jun 17 at 19:43







          • 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
            Jun 17 at 21:08






          • 1





            If you want to require callers to manually inline, instead of writing a wrapper function, then make it static const int len; The u8 will probably end up still costing you 4 bytes, because it's inside a struct that will get 4-byte alignment (assuming alignof(float) = 4. So the struct size will be a multiple of 4 bytes, because compilers always pad structs to a multiple of their alignof(), so in an array all the elements have correct alignment.

            – Peter Cordes
            Jun 17 at 21:51













          6












          6








          6







          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 Jun 17 at 22:45

























          answered Jun 17 at 9:34









          Adam ZahranAdam Zahran

          6177 silver badges23 bronze badges




          6177 silver badges23 bronze badges












          • 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
            Jun 17 at 9:38






          • 6





            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
            Jun 17 at 9:42






          • 3





            @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
            Jun 17 at 19:43







          • 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
            Jun 17 at 21:08






          • 1





            If you want to require callers to manually inline, instead of writing a wrapper function, then make it static const int len; The u8 will probably end up still costing you 4 bytes, because it's inside a struct that will get 4-byte alignment (assuming alignof(float) = 4. So the struct size will be a multiple of 4 bytes, because compilers always pad structs to a multiple of their alignof(), so in an array all the elements have correct alignment.

            – Peter Cordes
            Jun 17 at 21:51

















          • 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
            Jun 17 at 9:38






          • 6





            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
            Jun 17 at 9:42






          • 3





            @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
            Jun 17 at 19:43







          • 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
            Jun 17 at 21:08






          • 1





            If you want to require callers to manually inline, instead of writing a wrapper function, then make it static const int len; The u8 will probably end up still costing you 4 bytes, because it's inside a struct that will get 4-byte alignment (assuming alignof(float) = 4. So the struct size will be a multiple of 4 bytes, because compilers always pad structs to a multiple of their alignof(), so in an array all the elements have correct alignment.

            – Peter Cordes
            Jun 17 at 21:51
















          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
          Jun 17 at 9:38





          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
          Jun 17 at 9:38




          6




          6





          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
          Jun 17 at 9:42





          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
          Jun 17 at 9:42




          3




          3





          @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
          Jun 17 at 19:43






          @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
          Jun 17 at 19:43





          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
          Jun 17 at 21:08





          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
          Jun 17 at 21:08




          1




          1





          If you want to require callers to manually inline, instead of writing a wrapper function, then make it static const int len; The u8 will probably end up still costing you 4 bytes, because it's inside a struct that will get 4-byte alignment (assuming alignof(float) = 4. So the struct size will be a multiple of 4 bytes, because compilers always pad structs to a multiple of their alignof(), so in an array all the elements have correct alignment.

          – Peter Cordes
          Jun 17 at 21:51





          If you want to require callers to manually inline, instead of writing a wrapper function, then make it static const int len; The u8 will probably end up still costing you 4 bytes, because it's inside a struct that will get 4-byte alignment (assuming alignof(float) = 4. So the struct size will be a multiple of 4 bytes, because compilers always pad structs to a multiple of their alignof(), so in an array all the elements have correct alignment.

          – Peter Cordes
          Jun 17 at 21:51











          5














          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
            Jun 18 at 0:41






          • 1





            @PeterCordes I suppose getDerivedInfo must be virtual because this is the assumption done in the question (Objects are accessed though expression of type Base). The generic function would be so short that it will be inlined whatsoever, resulting in the same code (the expression that initialize length is a constant expression). Virtualization could be implemented by hand, like in the NikosC. answer, in a memory efficient way, but the compiler will not any more be able to perform devirtualization.

            – Oliv
            Jun 18 at 5:52











          • @PeterCordes What is laking to the language is a way to express that a specific non-static data member value in a base class is the same for all object that have the same dynamic type.

            – Oliv
            Jun 18 at 5:52















          5














          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
            Jun 18 at 0:41






          • 1





            @PeterCordes I suppose getDerivedInfo must be virtual because this is the assumption done in the question (Objects are accessed though expression of type Base). The generic function would be so short that it will be inlined whatsoever, resulting in the same code (the expression that initialize length is a constant expression). Virtualization could be implemented by hand, like in the NikosC. answer, in a memory efficient way, but the compiler will not any more be able to perform devirtualization.

            – Oliv
            Jun 18 at 5:52











          • @PeterCordes What is laking to the language is a way to express that a specific non-static data member value in a base class is the same for all object that have the same dynamic type.

            – Oliv
            Jun 18 at 5:52













          5












          5








          5







          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 Jun 17 at 8:58









          OlivOliv

          11.1k1 gold badge20 silver badges58 bronze badges




          11.1k1 gold badge20 silver badges58 bronze badges












          • 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
            Jun 18 at 0:41






          • 1





            @PeterCordes I suppose getDerivedInfo must be virtual because this is the assumption done in the question (Objects are accessed though expression of type Base). The generic function would be so short that it will be inlined whatsoever, resulting in the same code (the expression that initialize length is a constant expression). Virtualization could be implemented by hand, like in the NikosC. answer, in a memory efficient way, but the compiler will not any more be able to perform devirtualization.

            – Oliv
            Jun 18 at 5:52











          • @PeterCordes What is laking to the language is a way to express that a specific non-static data member value in a base class is the same for all object that have the same dynamic type.

            – Oliv
            Jun 18 at 5:52

















          • 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
            Jun 18 at 0:41






          • 1





            @PeterCordes I suppose getDerivedInfo must be virtual because this is the assumption done in the question (Objects are accessed though expression of type Base). The generic function would be so short that it will be inlined whatsoever, resulting in the same code (the expression that initialize length is a constant expression). Virtualization could be implemented by hand, like in the NikosC. answer, in a memory efficient way, but the compiler will not any more be able to perform devirtualization.

            – Oliv
            Jun 18 at 5:52











          • @PeterCordes What is laking to the language is a way to express that a specific non-static data member value in a base class is the same for all object that have the same dynamic type.

            – Oliv
            Jun 18 at 5:52
















          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
          Jun 18 at 0:41





          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
          Jun 18 at 0:41




          1




          1





          @PeterCordes I suppose getDerivedInfo must be virtual because this is the assumption done in the question (Objects are accessed though expression of type Base). The generic function would be so short that it will be inlined whatsoever, resulting in the same code (the expression that initialize length is a constant expression). Virtualization could be implemented by hand, like in the NikosC. answer, in a memory efficient way, but the compiler will not any more be able to perform devirtualization.

          – Oliv
          Jun 18 at 5:52





          @PeterCordes I suppose getDerivedInfo must be virtual because this is the assumption done in the question (Objects are accessed though expression of type Base). The generic function would be so short that it will be inlined whatsoever, resulting in the same code (the expression that initialize length is a constant expression). Virtualization could be implemented by hand, like in the NikosC. answer, in a memory efficient way, but the compiler will not any more be able to perform devirtualization.

          – Oliv
          Jun 18 at 5:52













          @PeterCordes What is laking to the language is a way to express that a specific non-static data member value in a base class is the same for all object that have the same dynamic type.

          – Oliv
          Jun 18 at 5:52





          @PeterCordes What is laking to the language is a way to express that a specific non-static data member value in a base class is the same for all object that have the same dynamic type.

          – Oliv
          Jun 18 at 5:52











          4














          Start with a vocabulary type:



          template<class T>
          struct span
          T* b = nullptr;
          T* e = nullptr;

          // these all do something reasonable:
          span()=default;
          span(span const&)=default;
          span& operator=(span const&)=default;

          // pair of pointers, or pointer and length:
          span( T* s, T* f ):b(s), e(f)
          span( T* s, size_t l ):span(s, s+l)

          // construct from an array of known length:
          template<size_t N>
          span( T(&arr)[N] ):span(arr, N)

          // Pointers are iterators:
          T* begin() const return b;
          T* end() const return e;

          // extended container-like utility functions:
          T* data() const return begin();
          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,
          // a span of const int can be constructed from
          // an array of non-const int.
          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);
          ;


          this type has been introduced to C++ std (with slight differences) via the GSL. The basic vocabulary type above is enough if you don't already have it.



          A span represents a "pointer" to a block of contiguous objects of known length.



          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





























            4














            Start with a vocabulary type:



            template<class T>
            struct span
            T* b = nullptr;
            T* e = nullptr;

            // these all do something reasonable:
            span()=default;
            span(span const&)=default;
            span& operator=(span const&)=default;

            // pair of pointers, or pointer and length:
            span( T* s, T* f ):b(s), e(f)
            span( T* s, size_t l ):span(s, s+l)

            // construct from an array of known length:
            template<size_t N>
            span( T(&arr)[N] ):span(arr, N)

            // Pointers are iterators:
            T* begin() const return b;
            T* end() const return e;

            // extended container-like utility functions:
            T* data() const return begin();
            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,
            // a span of const int can be constructed from
            // an array of non-const int.
            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);
            ;


            this type has been introduced to C++ std (with slight differences) via the GSL. The basic vocabulary type above is enough if you don't already have it.



            A span represents a "pointer" to a block of contiguous objects of known length.



            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



























              4












              4








              4







              Start with a vocabulary type:



              template<class T>
              struct span
              T* b = nullptr;
              T* e = nullptr;

              // these all do something reasonable:
              span()=default;
              span(span const&)=default;
              span& operator=(span const&)=default;

              // pair of pointers, or pointer and length:
              span( T* s, T* f ):b(s), e(f)
              span( T* s, size_t l ):span(s, s+l)

              // construct from an array of known length:
              template<size_t N>
              span( T(&arr)[N] ):span(arr, N)

              // Pointers are iterators:
              T* begin() const return b;
              T* end() const return e;

              // extended container-like utility functions:
              T* data() const return begin();
              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,
              // a span of const int can be constructed from
              // an array of non-const int.
              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);
              ;


              this type has been introduced to C++ std (with slight differences) via the GSL. The basic vocabulary type above is enough if you don't already have it.



              A span represents a "pointer" to a block of contiguous objects of known length.



              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;

              // these all do something reasonable:
              span()=default;
              span(span const&)=default;
              span& operator=(span const&)=default;

              // pair of pointers, or pointer and length:
              span( T* s, T* f ):b(s), e(f)
              span( T* s, size_t l ):span(s, s+l)

              // construct from an array of known length:
              template<size_t N>
              span( T(&arr)[N] ):span(arr, N)

              // Pointers are iterators:
              T* begin() const return b;
              T* end() const return e;

              // extended container-like utility functions:
              T* data() const return begin();
              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,
              // a span of const int can be constructed from
              // an array of non-const int.
              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);
              ;


              this type has been introduced to C++ std (with slight differences) via the GSL. The basic vocabulary type above is enough if you don't already have it.



              A span represents a "pointer" to a block of contiguous objects of known length.



              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 Jun 18 at 12:52

























              answered Jun 17 at 18:58









              Yakk - Adam NevraumontYakk - Adam Nevraumont

              193k21 gold badges210 silver badges399 bronze badges




              193k21 gold badges210 silver badges399 bronze badges





















                  3














                  So if you really want to keep your data organised the way it is, and I can see why you would in real life:



                  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





























                    3














                    So if you really want to keep your data organised the way it is, and I can see why you would in real life:



                    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



























                      3












                      3








                      3







                      So if you really want to keep your data organised the way it is, and I can see why you would in real life:



                      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















                      So if you really want to keep your data organised the way it is, and I can see why you would in real life:



                      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 Jun 18 at 10:09

























                      answered Jun 17 at 11:09









                      Gem TaylorGem Taylor

                      2,9202 silver badges22 bronze badges




                      2,9202 silver badges22 bronze badges





















                          3














                          How about CRTP + std::array? No extra variables, v-ptr or virtual function calls. std::array is a very thin wrapper around C style array. Empty base class optimization ensures no space is wasted. It looks "elegant" enough to me :)



                          template<typename Derived>
                          class BaseT

                          public:
                          struct SomeInfo

                          const char *name;
                          const f32_t value;
                          ;

                          void iterateInfo()

                          Derived* pDerived = static_cast<Derived*>(this);
                          for (const auto& i: pDerived->c_myInfo)

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


                          ;

                          class DerivedA : public BaseT<DerivedA>

                          public:
                          const std::array<SomeInfo,2> c_myInfo "NameA1", 1.1f, "NameA2", 1.2f ;
                          ;

                          class DerivedB : public BaseT<DerivedB>

                          public:
                          const std::array<SomeInfo, 3> c_myInfo "NameB1", 2.1f, "NameB2", 2.2f, "NameB2", 2.3f ;
                          ;





                          share|improve this answer










                          New contributor



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























                            3














                            How about CRTP + std::array? No extra variables, v-ptr or virtual function calls. std::array is a very thin wrapper around C style array. Empty base class optimization ensures no space is wasted. It looks "elegant" enough to me :)



                            template<typename Derived>
                            class BaseT

                            public:
                            struct SomeInfo

                            const char *name;
                            const f32_t value;
                            ;

                            void iterateInfo()

                            Derived* pDerived = static_cast<Derived*>(this);
                            for (const auto& i: pDerived->c_myInfo)

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


                            ;

                            class DerivedA : public BaseT<DerivedA>

                            public:
                            const std::array<SomeInfo,2> c_myInfo "NameA1", 1.1f, "NameA2", 1.2f ;
                            ;

                            class DerivedB : public BaseT<DerivedB>

                            public:
                            const std::array<SomeInfo, 3> c_myInfo "NameB1", 2.1f, "NameB2", 2.2f, "NameB2", 2.3f ;
                            ;





                            share|improve this answer










                            New contributor



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





















                              3












                              3








                              3







                              How about CRTP + std::array? No extra variables, v-ptr or virtual function calls. std::array is a very thin wrapper around C style array. Empty base class optimization ensures no space is wasted. It looks "elegant" enough to me :)



                              template<typename Derived>
                              class BaseT

                              public:
                              struct SomeInfo

                              const char *name;
                              const f32_t value;
                              ;

                              void iterateInfo()

                              Derived* pDerived = static_cast<Derived*>(this);
                              for (const auto& i: pDerived->c_myInfo)

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


                              ;

                              class DerivedA : public BaseT<DerivedA>

                              public:
                              const std::array<SomeInfo,2> c_myInfo "NameA1", 1.1f, "NameA2", 1.2f ;
                              ;

                              class DerivedB : public BaseT<DerivedB>

                              public:
                              const std::array<SomeInfo, 3> c_myInfo "NameB1", 2.1f, "NameB2", 2.2f, "NameB2", 2.3f ;
                              ;





                              share|improve this answer










                              New contributor



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









                              How about CRTP + std::array? No extra variables, v-ptr or virtual function calls. std::array is a very thin wrapper around C style array. Empty base class optimization ensures no space is wasted. It looks "elegant" enough to me :)



                              template<typename Derived>
                              class BaseT

                              public:
                              struct SomeInfo

                              const char *name;
                              const f32_t value;
                              ;

                              void iterateInfo()

                              Derived* pDerived = static_cast<Derived*>(this);
                              for (const auto& i: pDerived->c_myInfo)

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


                              ;

                              class DerivedA : public BaseT<DerivedA>

                              public:
                              const std::array<SomeInfo,2> c_myInfo "NameA1", 1.1f, "NameA2", 1.2f ;
                              ;

                              class DerivedB : public BaseT<DerivedB>

                              public:
                              const std::array<SomeInfo, 3> c_myInfo "NameB1", 2.1f, "NameB2", 2.2f, "NameB2", 2.3f ;
                              ;






                              share|improve this answer










                              New contributor



                              SPD 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 answer



                              share|improve this answer








                              edited Jun 18 at 14:57





















                              New contributor



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








                              answered Jun 18 at 13:21









                              SPDSPD

                              312 bronze badges




                              312 bronze badges




                              New contributor



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




                              New contributor




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























                                  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
                                    Jun 17 at 21:00











                                  • OP didn't share full code so it is a coin toss either way about whether the data is compile time constant or not.

                                    – Tanveer Badar
                                    Jun 18 at 8:42











                                  • Ok, but there's definitely zero point using vector<vector<T>> over array<vector<T>>. The outer array is definitely fixed-size because it has 1 entry per derived type.

                                    – Peter Cordes
                                    Jun 18 at 15:49















                                  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
                                    Jun 17 at 21:00











                                  • OP didn't share full code so it is a coin toss either way about whether the data is compile time constant or not.

                                    – Tanveer Badar
                                    Jun 18 at 8:42











                                  • Ok, but there's definitely zero point using vector<vector<T>> over array<vector<T>>. The outer array is definitely fixed-size because it has 1 entry per derived type.

                                    – Peter Cordes
                                    Jun 18 at 15:49













                                  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 Jun 18 at 0:01









                                  Peter Mortensen

                                  14.2k19 gold badges88 silver badges114 bronze badges




                                  14.2k19 gold badges88 silver badges114 bronze badges










                                  answered Jun 17 at 8:46









                                  Tanveer BadarTanveer Badar

                                  1,71013 silver badges20 bronze badges




                                  1,71013 silver badges20 bronze badges







                                  • 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
                                    Jun 17 at 21:00











                                  • OP didn't share full code so it is a coin toss either way about whether the data is compile time constant or not.

                                    – Tanveer Badar
                                    Jun 18 at 8:42











                                  • Ok, but there's definitely zero point using vector<vector<T>> over array<vector<T>>. The outer array is definitely fixed-size because it has 1 entry per derived type.

                                    – Peter Cordes
                                    Jun 18 at 15:49












                                  • 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
                                    Jun 17 at 21:00











                                  • OP didn't share full code so it is a coin toss either way about whether the data is compile time constant or not.

                                    – Tanveer Badar
                                    Jun 18 at 8:42











                                  • Ok, but there's definitely zero point using vector<vector<T>> over array<vector<T>>. The outer array is definitely fixed-size because it has 1 entry per derived type.

                                    – Peter Cordes
                                    Jun 18 at 15:49







                                  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
                                  Jun 17 at 21:00





                                  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
                                  Jun 17 at 21:00













                                  OP didn't share full code so it is a coin toss either way about whether the data is compile time constant or not.

                                  – Tanveer Badar
                                  Jun 18 at 8:42





                                  OP didn't share full code so it is a coin toss either way about whether the data is compile time constant or not.

                                  – Tanveer Badar
                                  Jun 18 at 8:42













                                  Ok, but there's definitely zero point using vector<vector<T>> over array<vector<T>>. The outer array is definitely fixed-size because it has 1 entry per derived type.

                                  – Peter Cordes
                                  Jun 18 at 15:49





                                  Ok, but there's definitely zero point using vector<vector<T>> over array<vector<T>>. The outer array is definitely fixed-size because it has 1 entry per derived type.

                                  – Peter Cordes
                                  Jun 18 at 15:49











                                  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 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 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 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 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 Jun 18 at 8:25

























                                      answered Jun 17 at 11:35









                                      darunedarune

                                      2,5928 silver badges26 bronze badges




                                      2,5928 silver badges26 bronze badges




















                                          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

                                          Grendel Contents Story Scholarship Depictions Notes References Navigation menu10.1093/notesj/gjn112Berserkeree

                                          Area configuration aggregation error after install Porto themeMagento 2.1 CE Installed but front/backend not loading/workingCSS not loading on page within Magento 2 pageCannot install module in Magento 2no commands defined in the “setup” namespace. in Magento2Magento 2: Static files are present but shows 404Why do i have to always run the commands to clean cache in Magento 2.1.8?Failure reason: 'Unable to unserialize value.'Error 500 after magento migrationIn production mode the site does not loadMagento 2 : Error 500 after installing

                                          Middle Expansion Olielle Resaix Definition: Uttering songs of triumph shouting with joy triumphant exulting Sejunction Journal 붙다 달 고급 품목 외출 The stretch trades the screeching tin. Definition: The act of speaking with a drawl a drawl Cough Sand Definition: An uproar a quarrel a noisy outbreak Shake Iron Publicize Horse House Baby 사과 Resaix Flaggy Jelly Temporary Unequaled Puppet A drop in the bucket Shrew 성격 회원 성질 미팅 The burn frames the tacky quality. Materialistic The smoke reduces the way. Yammoe Nondescript Cheek 얼굴 배 약하다 날리다 타다 The illegal country shows the iron. Help Rule Drearien Smoke Teaching Meaty Wasp Abraham Lincoln Jaws 진심 수리하다 Size Cork Idea Convert Think Lark John Lennon 거울 청소 군 추천하다 아이스크림