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++)
Has a life raft ever been successfully deployed on a modern commercial flight?
Cut the gold chain
Non-misogynistic way to say “asshole”?
Justifying Affordable Bespoke Spaceships
"Correct me if I'm wrong"
How to work with PETG? Settings, caveats, etc
Why is it easier to balance a non-moving bike standing up than sitting down?
How does DC work with natural 20?
Too early in the morning to have SODA?
Warnings using NDSolve on wave PDE. "Using maximum number of grid points" , "Warning: scaled local spatial error estimate"
What is "industrial ethernet"?
Subtract the Folded Matrix
Mathematically modelling RC circuit with a linear input
I just entered the USA without passport control at Atlanta airport
I found a password with hashcat, but it doesn't work
Can you use one creature for both convoke and delve for Hogaak?
Methodology: Writing unit tests for another developer
In the US, can a former president run again?
What is the highest voltage from the power supply a Raspberry Pi 3 B can handle without getting damaged?
Am I legally required to provide a (GPL licensed) source code even after a project is abandoned?
Designing a magic-compatible polearm
Umlaut character order when sorting
Rejecting an offer after accepting it just 10 days from date of joining
How long did the SR-71 take to get to cruising altitude?
How do I remove this inheritance-related code smell?
How do you set, clear, and toggle a single bit?Prefer composition over inheritance?How do I iterate over the words of a string?How can I profile C++ code running on Linux?Difference between private, public, and protected inheritancePython class inherits objectC++11 introduced a standardized memory model. What does it mean? And how is it going to affect C++ programming?What are the nuances of scope prototypal / prototypical inheritance in AngularJS?Why not inherit from List<T>?Downcasting best-practice (C++)
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty height:90px;width:728px;box-sizing:border-box;
I need to implement a lot of derived classes with different const member data. The data processing should be handled in the base class, but I can't find an elegant way to access the derived data. The code below is working, but I really don't like it.
The code needs to run in a small embedded environment so extensive usage of heap or fancy libraries like Boost are no option.
class Base
public:
struct SomeInfo
const char *name;
const f32_t value;
;
void iterateInfo()
// I would love to just write
// for(const auto& info : c_myInfo) ...
u8_t len = 0;
const auto *returnedInfo = getDerivedInfo(len);
for (int i = 0; i < len; i++)
DPRINTF("Name: %s - Value: %f n", returnedInfo[i].name, returnedInfo[i].value);
virtual const SomeInfo* getDerivedInfo(u8_t &length) = 0;
;
class DerivedA : public Base
public:
const SomeInfo c_myInfo[2] "NameA1", 1.1f, "NameA2", 1.2f ;
virtual const SomeInfo* getDerivedInfo(u8_t &length) override
// Duplicated code in every derived implementation....
length = sizeof(c_myInfo) / sizeof(c_myInfo[0]);
return c_myInfo;
;
class DerivedB : public Base
public:
const SomeInfo c_myInfo[3] "NameB1", 2.1f, "NameB2", 2.2f, "NameB2", 2.3f ;
virtual const SomeInfo *getDerivedInfo(u8_t &length) override
// Duplicated code in every derived implementation....
length = sizeof(c_myInfo) / sizeof(c_myInfo[0]);
return c_myInfo;
;
DerivedA instanceA;
DerivedB instanceB;
instanceA.iterateInfo();
instanceB.iterateInfo();
c++ c++11 inheritance
New contributor
add a comment |
I need to implement a lot of derived classes with different const member data. The data processing should be handled in the base class, but I can't find an elegant way to access the derived data. The code below is working, but I really don't like it.
The code needs to run in a small embedded environment so extensive usage of heap or fancy libraries like Boost are no option.
class Base
public:
struct SomeInfo
const char *name;
const f32_t value;
;
void iterateInfo()
// I would love to just write
// for(const auto& info : c_myInfo) ...
u8_t len = 0;
const auto *returnedInfo = getDerivedInfo(len);
for (int i = 0; i < len; i++)
DPRINTF("Name: %s - Value: %f n", returnedInfo[i].name, returnedInfo[i].value);
virtual const SomeInfo* getDerivedInfo(u8_t &length) = 0;
;
class DerivedA : public Base
public:
const SomeInfo c_myInfo[2] "NameA1", 1.1f, "NameA2", 1.2f ;
virtual const SomeInfo* getDerivedInfo(u8_t &length) override
// Duplicated code in every derived implementation....
length = sizeof(c_myInfo) / sizeof(c_myInfo[0]);
return c_myInfo;
;
class DerivedB : public Base
public:
const SomeInfo c_myInfo[3] "NameB1", 2.1f, "NameB2", 2.2f, "NameB2", 2.3f ;
virtual const SomeInfo *getDerivedInfo(u8_t &length) override
// Duplicated code in every derived implementation....
length = sizeof(c_myInfo) / sizeof(c_myInfo[0]);
return c_myInfo;
;
DerivedA instanceA;
DerivedB instanceB;
instanceA.iterateInfo();
instanceB.iterateInfo();
c++ c++11 inheritance
New contributor
IsBase
supposed to be able to be instantiable directly, or only the derived types?
– Nikos C.
15 hours ago
No, I only instanciate the derived classes.
– SirNobbyNobbs
15 hours ago
@NikosC.Base is abstract, can't create instances of it.
– Tanveer Badar
15 hours ago
1
IfSomeInfo c_myInfo[3]
isconst
and has a compile-time constant initializer, why do you have it inside the object instead ofstatic
? 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 forenum
..)
– Peter Cordes
3 hours ago
I'd give you +1 for using the word "smell" and another for your nom de guerre (which relates extensively to the word "smell"), but Stack Exchange says I'm supposed to act more responsibly than that. Oh, what the heck. +1.
– JBH
2 hours ago
add a comment |
I need to implement a lot of derived classes with different const member data. The data processing should be handled in the base class, but I can't find an elegant way to access the derived data. The code below is working, but I really don't like it.
The code needs to run in a small embedded environment so extensive usage of heap or fancy libraries like Boost are no option.
class Base
public:
struct SomeInfo
const char *name;
const f32_t value;
;
void iterateInfo()
// I would love to just write
// for(const auto& info : c_myInfo) ...
u8_t len = 0;
const auto *returnedInfo = getDerivedInfo(len);
for (int i = 0; i < len; i++)
DPRINTF("Name: %s - Value: %f n", returnedInfo[i].name, returnedInfo[i].value);
virtual const SomeInfo* getDerivedInfo(u8_t &length) = 0;
;
class DerivedA : public Base
public:
const SomeInfo c_myInfo[2] "NameA1", 1.1f, "NameA2", 1.2f ;
virtual const SomeInfo* getDerivedInfo(u8_t &length) override
// Duplicated code in every derived implementation....
length = sizeof(c_myInfo) / sizeof(c_myInfo[0]);
return c_myInfo;
;
class DerivedB : public Base
public:
const SomeInfo c_myInfo[3] "NameB1", 2.1f, "NameB2", 2.2f, "NameB2", 2.3f ;
virtual const SomeInfo *getDerivedInfo(u8_t &length) override
// Duplicated code in every derived implementation....
length = sizeof(c_myInfo) / sizeof(c_myInfo[0]);
return c_myInfo;
;
DerivedA instanceA;
DerivedB instanceB;
instanceA.iterateInfo();
instanceB.iterateInfo();
c++ c++11 inheritance
New contributor
I need to implement a lot of derived classes with different const member data. The data processing should be handled in the base class, but I can't find an elegant way to access the derived data. The code below is working, but I really don't like it.
The code needs to run in a small embedded environment so extensive usage of heap or fancy libraries like Boost are no option.
class Base
public:
struct SomeInfo
const char *name;
const f32_t value;
;
void iterateInfo()
// I would love to just write
// for(const auto& info : c_myInfo) ...
u8_t len = 0;
const auto *returnedInfo = getDerivedInfo(len);
for (int i = 0; i < len; i++)
DPRINTF("Name: %s - Value: %f n", returnedInfo[i].name, returnedInfo[i].value);
virtual const SomeInfo* getDerivedInfo(u8_t &length) = 0;
;
class DerivedA : public Base
public:
const SomeInfo c_myInfo[2] "NameA1", 1.1f, "NameA2", 1.2f ;
virtual const SomeInfo* getDerivedInfo(u8_t &length) override
// Duplicated code in every derived implementation....
length = sizeof(c_myInfo) / sizeof(c_myInfo[0]);
return c_myInfo;
;
class DerivedB : public Base
public:
const SomeInfo c_myInfo[3] "NameB1", 2.1f, "NameB2", 2.2f, "NameB2", 2.3f ;
virtual const SomeInfo *getDerivedInfo(u8_t &length) override
// Duplicated code in every derived implementation....
length = sizeof(c_myInfo) / sizeof(c_myInfo[0]);
return c_myInfo;
;
DerivedA instanceA;
DerivedB instanceB;
instanceA.iterateInfo();
instanceB.iterateInfo();
c++ c++11 inheritance
c++ c++11 inheritance
New contributor
New contributor
edited 10 mins ago
Peter Mortensen
14.2k1988114
14.2k1988114
New contributor
asked 15 hours ago
SirNobbyNobbsSirNobbyNobbs
765
765
New contributor
New contributor
IsBase
supposed to be able to be instantiable directly, or only the derived types?
– Nikos C.
15 hours ago
No, I only instanciate the derived classes.
– SirNobbyNobbs
15 hours ago
@NikosC.Base is abstract, can't create instances of it.
– Tanveer Badar
15 hours ago
1
IfSomeInfo c_myInfo[3]
isconst
and has a compile-time constant initializer, why do you have it inside the object instead ofstatic
? 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 forenum
..)
– Peter Cordes
3 hours ago
I'd give you +1 for using the word "smell" and another for your nom de guerre (which relates extensively to the word "smell"), but Stack Exchange says I'm supposed to act more responsibly than that. Oh, what the heck. +1.
– JBH
2 hours ago
add a comment |
IsBase
supposed to be able to be instantiable directly, or only the derived types?
– Nikos C.
15 hours ago
No, I only instanciate the derived classes.
– SirNobbyNobbs
15 hours ago
@NikosC.Base is abstract, can't create instances of it.
– Tanveer Badar
15 hours ago
1
IfSomeInfo c_myInfo[3]
isconst
and has a compile-time constant initializer, why do you have it inside the object instead ofstatic
? 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 forenum
..)
– Peter Cordes
3 hours ago
I'd give you +1 for using the word "smell" and another for your nom de guerre (which relates extensively to the word "smell"), but Stack Exchange says I'm supposed to act more responsibly than that. Oh, what the heck. +1.
– JBH
2 hours ago
Is
Base
supposed to be able to be instantiable directly, or only the derived types?– Nikos C.
15 hours ago
Is
Base
supposed to be able to be instantiable directly, or only the derived types?– Nikos C.
15 hours ago
No, I only instanciate the derived classes.
– SirNobbyNobbs
15 hours ago
No, I only instanciate the derived classes.
– SirNobbyNobbs
15 hours ago
@NikosC.Base is abstract, can't create instances of it.
– Tanveer Badar
15 hours ago
@NikosC.Base is abstract, can't create instances of it.
– Tanveer Badar
15 hours ago
1
1
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
3 hours ago
If
SomeInfo c_myInfo[3]
is const
and has a compile-time constant initializer, why do you have it inside the object instead of static
? Do you only create one instance of each type, so there isn't actually duplication of the pointers + floats? (Also a string key/value array doesn't sound great for efficiency if you're using it as a dictionary, but that's a separate issue. Sounds like a job for enum
..)– Peter Cordes
3 hours ago
I'd give you +1 for using the word "smell" and another for your nom de guerre (which relates extensively to the word "smell"), but Stack Exchange says I'm supposed to act more responsibly than that. Oh, what the heck. +1.
– JBH
2 hours ago
I'd give you +1 for using the word "smell" and another for your nom de guerre (which relates extensively to the word "smell"), but Stack Exchange says I'm supposed to act more responsibly than that. Oh, what the heck. +1.
– JBH
2 hours ago
add a comment |
8 Answers
8
active
oldest
votes
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.
1
This grows the size of every object by 1 pointer + 1int
. 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
3 hours ago
add a comment |
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.
add a comment |
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 ;
;
add a comment |
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
.
add a comment |
Start with a vocabulary type.
template<class T>
struct span
T* b = nullptr;
T* e = nullptr;
span( T* s, T* f ):b(s), e(f)
span( T* s, size_t l ):span(s, s+l)
template<size_t N>
span( T(&arr)[N] ):span(arr, N)
T* begin() const return b;
T* end() const return e;
size_t size() const return end()-begin();
bool empty() const return size()==0;
T& front() const return *begin();
T& back() const return *(end()-1);
;
// this is just here for the other array ctor:
template<class T>
struct span<T const>
T const* b = nullptr;
T const* e = nullptr;
span( T const* s, T const* f ):b(s), e(f)
span( T const* s, size_t l ):span(s, s+l)
template<size_t N>
span( T const(&arr)[N] ):span(arr, N)
template<size_t N>
span( T(&arr)[N] ):span(arr, N)
T const* begin() const return b;
T const* end() const return e;
size_t size() const return end()-begin();
bool empty() const return size()==0;
T const& front() const return *begin();
T const& back() const return *(end()-1);
;
now we can talk about a span<char>
.
class Base
public:
void iterateInfo()
for(const auto& info : c_mySpan)
DPRINTF("Name: %s - Value: %f n", info.name, info.value);
private:
span<const char> c_mySpan;
Base( span<const char> s ):c_mySpan(s)
Base(Base const&)=delete; // probably unsafe
;
now your derived looks like:
class DerivedA : public Base
public:
const SomeInfo c_myInfo[2] "NameA1", 1.1f, "NameA2", 1.2f ;
DerivedA() : Base(c_myInfo)
;
This has overhead of 2 pointers per Base
. A vtable uses 1 pointer, makes your type abstract, adds indirection, adds 1 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.
add a comment |
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);
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
14 hours ago
3
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
14 hours ago
@SirNobbyNobbs "The base class is already several hundred lines of code" - That is more of a code smell than anything that your simplified example might highlight,
– Goyo
4 hours ago
1
There's no point having au8_t len = 3;
member in each struct; the length of theinfo[]
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 ofA
andB
that passes the right args to a commonprocessData
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
2 hours ago
@PeterCordes Well I thought of that. But in order to retrieve the size we would need a function. This function could either be templated or repeated. But since I'm going for simplicity and avoiding code generation here I decided a little extra u8_t in the struct will harm no one :)
– Adam Zahran
2 hours ago
|
show 2 more comments
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();
2
Why aconst std::vector
for compile-time-constant data with known fixed size? Seems like a job for a flat 1Dstd::array<SomeInfo>
with each derived class knowing the right start index + offset. (GetIndex returns astd::pair<int,int>
). Or astd::array<std::vector<SomeInfo>>
. Or maybe implicit lengths by using a flat array ofSomeInfo
objects withnullptr
terminators for the end of each sub-array, if you only ever want to iterate in order.
– Peter Cordes
3 hours ago
add a comment |
Just make the virtual function return a reference to the data directly (you need to change to vector then - not possible with array or C style array types with different sizes):
virtual const std::vector<SomeInfo>& getDerivedInfo() = 0;
or if pointers are the only feasible option, as a pointer range (iterators/range adapter would be preferred though if possible - more on that):
virtual std::pair<SomeInfo*, SomeInfo*> getDerivedInfo() = 0;
To make this last method work with range-based for loop: one way is to make a small 'range view' type that has the functions begin()/end()
- essential a pair with begin()/end()
Example:
template<class T>
struct ptr_range
std::pair<T*, T*> range_;
auto begin()return range_.begin();
auto end()return range_.end();
;
Then construct it with:
virtual const ptr_range<SomeInfo> getDerivedInfo() override
return std::begin(c_myInfo), std::end(c_myInfo);
It is easy to make it non-template if a template is not desired.
add a comment |
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.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f56627438%2fhow-do-i-remove-this-inheritance-related-code-smell%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
8 Answers
8
active
oldest
votes
8 Answers
8
active
oldest
votes
active
oldest
votes
active
oldest
votes
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.
1
This grows the size of every object by 1 pointer + 1int
. 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
3 hours ago
add a comment |
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.
1
This grows the size of every object by 1 pointer + 1int
. 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
3 hours ago
add a comment |
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.
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.
edited 15 hours ago
answered 15 hours ago
Nikos C.Nikos C.
38k54171
38k54171
1
This grows the size of every object by 1 pointer + 1int
. 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
3 hours ago
add a comment |
1
This grows the size of every object by 1 pointer + 1int
. 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
3 hours ago
1
1
This grows the size of every object by 1 pointer + 1
int
. If you have many more instances of these objects than you do derived types, the OP's solution uses less total memory (data + code). They mention they're in a memory-constrained embedded environment. On a high-end system, the extra level of indirection is still a downside. (At least the pointer will typically be in the same cache line as the start of the data and the vtable pointer, so the extra latency is just 1 L1d cache ~load-use latency. Not an extra cache miss)– Peter Cordes
3 hours ago
This grows the size of every object by 1 pointer + 1
int
. If you have many more instances of these objects than you do derived types, the OP's solution uses less total memory (data + code). They mention they're in a memory-constrained embedded environment. On a high-end system, the extra level of indirection is still a downside. (At least the pointer will typically be in the same cache line as the start of the data and the vtable pointer, so the extra latency is just 1 L1d cache ~load-use latency. Not an extra cache miss)– Peter Cordes
3 hours ago
add a comment |
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.
add a comment |
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.
add a comment |
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.
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.
edited 8 mins ago
Peter Mortensen
14.2k1988114
14.2k1988114
answered 15 hours ago
DeiDeiDeiDei
6,40653656
6,40653656
add a comment |
add a comment |
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 ;
;
add a comment |
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 ;
;
add a comment |
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 ;
;
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 ;
;
answered 15 hours ago
OlivOliv
11k12058
11k12058
add a comment |
add a comment |
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
.
add a comment |
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
.
add a comment |
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
.
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
.
edited 2 mins ago
Peter Mortensen
14.2k1988114
14.2k1988114
answered 12 hours ago
Gem TaylorGem Taylor
2,905222
2,905222
add a comment |
add a comment |
Start with a vocabulary type.
template<class T>
struct span
T* b = nullptr;
T* e = nullptr;
span( T* s, T* f ):b(s), e(f)
span( T* s, size_t l ):span(s, s+l)
template<size_t N>
span( T(&arr)[N] ):span(arr, N)
T* begin() const return b;
T* end() const return e;
size_t size() const return end()-begin();
bool empty() const return size()==0;
T& front() const return *begin();
T& back() const return *(end()-1);
;
// this is just here for the other array ctor:
template<class T>
struct span<T const>
T const* b = nullptr;
T const* e = nullptr;
span( T const* s, T const* f ):b(s), e(f)
span( T const* s, size_t l ):span(s, s+l)
template<size_t N>
span( T const(&arr)[N] ):span(arr, N)
template<size_t N>
span( T(&arr)[N] ):span(arr, N)
T const* begin() const return b;
T const* end() const return e;
size_t size() const return end()-begin();
bool empty() const return size()==0;
T const& front() const return *begin();
T const& back() const return *(end()-1);
;
now we can talk about a span<char>
.
class Base
public:
void iterateInfo()
for(const auto& info : c_mySpan)
DPRINTF("Name: %s - Value: %f n", info.name, info.value);
private:
span<const char> c_mySpan;
Base( span<const char> s ):c_mySpan(s)
Base(Base const&)=delete; // probably unsafe
;
now your derived looks like:
class DerivedA : public Base
public:
const SomeInfo c_myInfo[2] "NameA1", 1.1f, "NameA2", 1.2f ;
DerivedA() : Base(c_myInfo)
;
This has overhead of 2 pointers per Base
. A vtable uses 1 pointer, makes your type abstract, adds indirection, adds 1 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.
add a comment |
Start with a vocabulary type.
template<class T>
struct span
T* b = nullptr;
T* e = nullptr;
span( T* s, T* f ):b(s), e(f)
span( T* s, size_t l ):span(s, s+l)
template<size_t N>
span( T(&arr)[N] ):span(arr, N)
T* begin() const return b;
T* end() const return e;
size_t size() const return end()-begin();
bool empty() const return size()==0;
T& front() const return *begin();
T& back() const return *(end()-1);
;
// this is just here for the other array ctor:
template<class T>
struct span<T const>
T const* b = nullptr;
T const* e = nullptr;
span( T const* s, T const* f ):b(s), e(f)
span( T const* s, size_t l ):span(s, s+l)
template<size_t N>
span( T const(&arr)[N] ):span(arr, N)
template<size_t N>
span( T(&arr)[N] ):span(arr, N)
T const* begin() const return b;
T const* end() const return e;
size_t size() const return end()-begin();
bool empty() const return size()==0;
T const& front() const return *begin();
T const& back() const return *(end()-1);
;
now we can talk about a span<char>
.
class Base
public:
void iterateInfo()
for(const auto& info : c_mySpan)
DPRINTF("Name: %s - Value: %f n", info.name, info.value);
private:
span<const char> c_mySpan;
Base( span<const char> s ):c_mySpan(s)
Base(Base const&)=delete; // probably unsafe
;
now your derived looks like:
class DerivedA : public Base
public:
const SomeInfo c_myInfo[2] "NameA1", 1.1f, "NameA2", 1.2f ;
DerivedA() : Base(c_myInfo)
;
This has overhead of 2 pointers per Base
. A vtable uses 1 pointer, makes your type abstract, adds indirection, adds 1 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.
add a comment |
Start with a vocabulary type.
template<class T>
struct span
T* b = nullptr;
T* e = nullptr;
span( T* s, T* f ):b(s), e(f)
span( T* s, size_t l ):span(s, s+l)
template<size_t N>
span( T(&arr)[N] ):span(arr, N)
T* begin() const return b;
T* end() const return e;
size_t size() const return end()-begin();
bool empty() const return size()==0;
T& front() const return *begin();
T& back() const return *(end()-1);
;
// this is just here for the other array ctor:
template<class T>
struct span<T const>
T const* b = nullptr;
T const* e = nullptr;
span( T const* s, T const* f ):b(s), e(f)
span( T const* s, size_t l ):span(s, s+l)
template<size_t N>
span( T const(&arr)[N] ):span(arr, N)
template<size_t N>
span( T(&arr)[N] ):span(arr, N)
T const* begin() const return b;
T const* end() const return e;
size_t size() const return end()-begin();
bool empty() const return size()==0;
T const& front() const return *begin();
T const& back() const return *(end()-1);
;
now we can talk about a span<char>
.
class Base
public:
void iterateInfo()
for(const auto& info : c_mySpan)
DPRINTF("Name: %s - Value: %f n", info.name, info.value);
private:
span<const char> c_mySpan;
Base( span<const char> s ):c_mySpan(s)
Base(Base const&)=delete; // probably unsafe
;
now your derived looks like:
class DerivedA : public Base
public:
const SomeInfo c_myInfo[2] "NameA1", 1.1f, "NameA2", 1.2f ;
DerivedA() : Base(c_myInfo)
;
This has overhead of 2 pointers per Base
. A vtable uses 1 pointer, makes your type abstract, adds indirection, adds 1 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.
Start with a vocabulary type.
template<class T>
struct span
T* b = nullptr;
T* e = nullptr;
span( T* s, T* f ):b(s), e(f)
span( T* s, size_t l ):span(s, s+l)
template<size_t N>
span( T(&arr)[N] ):span(arr, N)
T* begin() const return b;
T* end() const return e;
size_t size() const return end()-begin();
bool empty() const return size()==0;
T& front() const return *begin();
T& back() const return *(end()-1);
;
// this is just here for the other array ctor:
template<class T>
struct span<T const>
T const* b = nullptr;
T const* e = nullptr;
span( T const* s, T const* f ):b(s), e(f)
span( T const* s, size_t l ):span(s, s+l)
template<size_t N>
span( T const(&arr)[N] ):span(arr, N)
template<size_t N>
span( T(&arr)[N] ):span(arr, N)
T const* begin() const return b;
T const* end() const return e;
size_t size() const return end()-begin();
bool empty() const return size()==0;
T const& front() const return *begin();
T const& back() const return *(end()-1);
;
now we can talk about a span<char>
.
class Base
public:
void iterateInfo()
for(const auto& info : c_mySpan)
DPRINTF("Name: %s - Value: %f n", info.name, info.value);
private:
span<const char> c_mySpan;
Base( span<const char> s ):c_mySpan(s)
Base(Base const&)=delete; // probably unsafe
;
now your derived looks like:
class DerivedA : public Base
public:
const SomeInfo c_myInfo[2] "NameA1", 1.1f, "NameA2", 1.2f ;
DerivedA() : Base(c_myInfo)
;
This has overhead of 2 pointers per Base
. A vtable uses 1 pointer, makes your type abstract, adds indirection, adds 1 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.
answered 5 hours ago
Yakk - Adam NevraumontYakk - Adam Nevraumont
193k21204399
193k21204399
add a comment |
add a comment |
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);
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
14 hours ago
3
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
14 hours ago
@SirNobbyNobbs "The base class is already several hundred lines of code" - That is more of a code smell than anything that your simplified example might highlight,
– Goyo
4 hours ago
1
There's no point having au8_t len = 3;
member in each struct; the length of theinfo[]
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 ofA
andB
that passes the right args to a commonprocessData
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
2 hours ago
@PeterCordes Well I thought of that. But in order to retrieve the size we would need a function. This function could either be templated or repeated. But since I'm going for simplicity and avoiding code generation here I decided a little extra u8_t in the struct will harm no one :)
– Adam Zahran
2 hours ago
|
show 2 more comments
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);
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
14 hours ago
3
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
14 hours ago
@SirNobbyNobbs "The base class is already several hundred lines of code" - That is more of a code smell than anything that your simplified example might highlight,
– Goyo
4 hours ago
1
There's no point having au8_t len = 3;
member in each struct; the length of theinfo[]
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 ofA
andB
that passes the right args to a commonprocessData
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
2 hours ago
@PeterCordes Well I thought of that. But in order to retrieve the size we would need a function. This function could either be templated or repeated. But since I'm going for simplicity and avoiding code generation here I decided a little extra u8_t in the struct will harm no one :)
– Adam Zahran
2 hours ago
|
show 2 more comments
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);
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);
edited 1 hour ago
answered 14 hours ago
Adam ZahranAdam Zahran
557622
557622
Given the provided example you are right.But this is just a simplification to highlight my problem. The base class is already several hundred lines of code and every derived class also has much more inside than just the const info.
– SirNobbyNobbs
14 hours ago
3
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
14 hours ago
@SirNobbyNobbs "The base class is already several hundred lines of code" - That is more of a code smell than anything that your simplified example might highlight,
– Goyo
4 hours ago
1
There's no point having au8_t len = 3;
member in each struct; the length of theinfo[]
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 ofA
andB
that passes the right args to a commonprocessData
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
2 hours ago
@PeterCordes Well I thought of that. But in order to retrieve the size we would need a function. This function could either be templated or repeated. But since I'm going for simplicity and avoiding code generation here I decided a little extra u8_t in the struct will harm no one :)
– Adam Zahran
2 hours ago
|
show 2 more comments
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
14 hours ago
3
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
14 hours ago
@SirNobbyNobbs "The base class is already several hundred lines of code" - That is more of a code smell than anything that your simplified example might highlight,
– Goyo
4 hours ago
1
There's no point having au8_t len = 3;
member in each struct; the length of theinfo[]
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 ofA
andB
that passes the right args to a commonprocessData
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
2 hours ago
@PeterCordes Well I thought of that. But in order to retrieve the size we would need a function. This function could either be templated or repeated. But since I'm going for simplicity and avoiding code generation here I decided a little extra u8_t in the struct will harm no one :)
– Adam Zahran
2 hours ago
Given the provided example you are right.But this is just a simplification to highlight my problem. The base class is already several hundred lines of code and every derived class also has much more inside than just the const info.
– SirNobbyNobbs
14 hours ago
Given the provided example you are right.But this is just a simplification to highlight my problem. The base class is already several hundred lines of code and every derived class also has much more inside than just the const info.
– SirNobbyNobbs
14 hours ago
3
3
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
14 hours ago
Well, I can imagine. All I can suggest is to use composition instead of inheritance along with simple functions. Coding can be a simple and pleasurable experience. We just complicate everything for some reason :)
– Adam Zahran
14 hours ago
@SirNobbyNobbs "The base class is already several hundred lines of code" - That is more of a code smell than anything that your simplified example might highlight,
– Goyo
4 hours ago
@SirNobbyNobbs "The base class is already several hundred lines of code" - That is more of a code smell than anything that your simplified example might highlight,
– Goyo
4 hours ago
1
1
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
2 hours ago
There's no point having a
u8_t len = 3;
member in each struct; the length of the info[]
array member is already statically known as part of the derived type. @SirNobbyNobbs: What you could do is have a small inline wrapper in each of A
and B
that passes the right args to a common processData
function. It can be virtual if you need it to be, but letting it inline into each call site when you have full type info is good. (final
on the derived type functions allows that in more cases.)– Peter Cordes
2 hours ago
@PeterCordes Well I thought of that. But in order to retrieve the size we would need a function. This function could either be templated or repeated. But since I'm going for simplicity and avoiding code generation here I decided a little extra u8_t in the struct will harm no one :)
– Adam Zahran
2 hours ago
@PeterCordes Well I thought of that. But in order to retrieve the size we would need a function. This function could either be templated or repeated. But since I'm going for simplicity and avoiding code generation here I decided a little extra u8_t in the struct will harm no one :)
– Adam Zahran
2 hours ago
|
show 2 more comments
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();
2
Why aconst std::vector
for compile-time-constant data with known fixed size? Seems like a job for a flat 1Dstd::array<SomeInfo>
with each derived class knowing the right start index + offset. (GetIndex returns astd::pair<int,int>
). Or astd::array<std::vector<SomeInfo>>
. Or maybe implicit lengths by using a flat array ofSomeInfo
objects withnullptr
terminators for the end of each sub-array, if you only ever want to iterate in order.
– Peter Cordes
3 hours ago
add a comment |
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();
2
Why aconst std::vector
for compile-time-constant data with known fixed size? Seems like a job for a flat 1Dstd::array<SomeInfo>
with each derived class knowing the right start index + offset. (GetIndex returns astd::pair<int,int>
). Or astd::array<std::vector<SomeInfo>>
. Or maybe implicit lengths by using a flat array ofSomeInfo
objects withnullptr
terminators for the end of each sub-array, if you only ever want to iterate in order.
– Peter Cordes
3 hours ago
add a comment |
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();
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();
edited 5 mins ago
Peter Mortensen
14.2k1988114
14.2k1988114
answered 15 hours ago
Tanveer BadarTanveer Badar
1,6861320
1,6861320
2
Why aconst std::vector
for compile-time-constant data with known fixed size? Seems like a job for a flat 1Dstd::array<SomeInfo>
with each derived class knowing the right start index + offset. (GetIndex returns astd::pair<int,int>
). Or astd::array<std::vector<SomeInfo>>
. Or maybe implicit lengths by using a flat array ofSomeInfo
objects withnullptr
terminators for the end of each sub-array, if you only ever want to iterate in order.
– Peter Cordes
3 hours ago
add a comment |
2
Why aconst std::vector
for compile-time-constant data with known fixed size? Seems like a job for a flat 1Dstd::array<SomeInfo>
with each derived class knowing the right start index + offset. (GetIndex returns astd::pair<int,int>
). Or astd::array<std::vector<SomeInfo>>
. Or maybe implicit lengths by using a flat array ofSomeInfo
objects withnullptr
terminators for the end of each sub-array, if you only ever want to iterate in order.
– Peter Cordes
3 hours ago
2
2
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
3 hours ago
Why a
const std::vector
for compile-time-constant data with known fixed size? Seems like a job for a flat 1D std::array<SomeInfo>
with each derived class knowing the right start index + offset. (GetIndex returns a std::pair<int,int>
). Or a std::array<std::vector<SomeInfo>>
. Or maybe implicit lengths by using a flat array of SomeInfo
objects with nullptr
terminators for the end of each sub-array, if you only ever want to iterate in order.– Peter Cordes
3 hours ago
add a comment |
Just make the virtual function return a reference to the data directly (you need to change to vector then - not possible with array or C style array types with different sizes):
virtual const std::vector<SomeInfo>& getDerivedInfo() = 0;
or if pointers are the only feasible option, as a pointer range (iterators/range adapter would be preferred though if possible - more on that):
virtual std::pair<SomeInfo*, SomeInfo*> getDerivedInfo() = 0;
To make this last method work with range-based for loop: one way is to make a small 'range view' type that has the functions begin()/end()
- essential a pair with begin()/end()
Example:
template<class T>
struct ptr_range
std::pair<T*, T*> range_;
auto begin()return range_.begin();
auto end()return range_.end();
;
Then construct it with:
virtual const ptr_range<SomeInfo> getDerivedInfo() override
return std::begin(c_myInfo), std::end(c_myInfo);
It is easy to make it non-template if a template is not desired.
add a comment |
Just make the virtual function return a reference to the data directly (you need to change to vector then - not possible with array or C style array types with different sizes):
virtual const std::vector<SomeInfo>& getDerivedInfo() = 0;
or if pointers are the only feasible option, as a pointer range (iterators/range adapter would be preferred though if possible - more on that):
virtual std::pair<SomeInfo*, SomeInfo*> getDerivedInfo() = 0;
To make this last method work with range-based for loop: one way is to make a small 'range view' type that has the functions begin()/end()
- essential a pair with begin()/end()
Example:
template<class T>
struct ptr_range
std::pair<T*, T*> range_;
auto begin()return range_.begin();
auto end()return range_.end();
;
Then construct it with:
virtual const ptr_range<SomeInfo> getDerivedInfo() override
return std::begin(c_myInfo), std::end(c_myInfo);
It is easy to make it non-template if a template is not desired.
add a comment |
Just make the virtual function return a reference to the data directly (you need to change to vector then - not possible with array or C style array types with different sizes):
virtual const std::vector<SomeInfo>& getDerivedInfo() = 0;
or if pointers are the only feasible option, as a pointer range (iterators/range adapter would be preferred though if possible - more on that):
virtual std::pair<SomeInfo*, SomeInfo*> getDerivedInfo() = 0;
To make this last method work with range-based for loop: one way is to make a small 'range view' type that has the functions begin()/end()
- essential a pair with begin()/end()
Example:
template<class T>
struct ptr_range
std::pair<T*, T*> range_;
auto begin()return range_.begin();
auto end()return range_.end();
;
Then construct it with:
virtual const ptr_range<SomeInfo> getDerivedInfo() override
return std::begin(c_myInfo), std::end(c_myInfo);
It is easy to make it non-template if a template is not desired.
Just make the virtual function return a reference to the data directly (you need to change to vector then - not possible with array or C style array types with different sizes):
virtual const std::vector<SomeInfo>& getDerivedInfo() = 0;
or if pointers are the only feasible option, as a pointer range (iterators/range adapter would be preferred though if possible - more on that):
virtual std::pair<SomeInfo*, SomeInfo*> getDerivedInfo() = 0;
To make this last method work with range-based for loop: one way is to make a small 'range view' type that has the functions begin()/end()
- essential a pair with begin()/end()
Example:
template<class T>
struct ptr_range
std::pair<T*, T*> range_;
auto begin()return range_.begin();
auto end()return range_.end();
;
Then construct it with:
virtual const ptr_range<SomeInfo> getDerivedInfo() override
return std::begin(c_myInfo), std::end(c_myInfo);
It is easy to make it non-template if a template is not desired.
edited 1 min ago
Peter Mortensen
14.2k1988114
14.2k1988114
answered 12 hours ago
darunedarune
2,535826
2,535826
add a comment |
add a comment |
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.
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.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
Is
Base
supposed to be able to be instantiable directly, or only the derived types?– Nikos C.
15 hours ago
No, I only instanciate the derived classes.
– SirNobbyNobbs
15 hours ago
@NikosC.Base is abstract, can't create instances of it.
– Tanveer Badar
15 hours ago
1
If
SomeInfo c_myInfo[3]
isconst
and has a compile-time constant initializer, why do you have it inside the object instead ofstatic
? 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 forenum
..)– Peter Cordes
3 hours ago
I'd give you +1 for using the word "smell" and another for your nom de guerre (which relates extensively to the word "smell"), but Stack Exchange says I'm supposed to act more responsibly than that. Oh, what the heck. +1.
– JBH
2 hours ago