Related
I have a class with different functions. This class uses two typedef structs.
typedef struct {
struct head;
struct data1;
} data_struct_1;
typedef struct {
struct head;
struct data2;
} data_struct_2;
If a bool is true the data_struct_2 should be used, otherwise data_struct_1. The boolean is passed, when the object is created and stored as a membervariable in the constructor.
There are now different functions and declarations like
int calc_mean(data_struct_1 ds, int a){ ... }
or
data_struct_1 ds;
ds.data1 = 5;
Is there an elegant way, which allows me to switch between those two structs without implementing dozens of if-cases and duplicate all the relevant functions with the corresponding struct in the header?
First I was thinking about inheritance and overwritting relevant functions if bool is true.. but the header in the functions differs, so this wouldnt work
Maybe there is something like a placeholder or template, but i'm pretty new to c++
If boolean is compile-time value, std::conditional might be used:
template <boolean B>
using my_type = std::conditional_t<B, data_struct_1, data_struct_2>;
but it seems the boolean is runtime value, so I would use std::variant and std::visit:
class C
{
using Data = std::variant<data_struct_1, data_struct_2>;
Data m_data;
public:
C(bool b) : m_data{b ? Data{data_struct_1{}} : Data{data_struct_2{}}} {}
auto mean() const {
return std::visit([](const auto& data){ return calc_mean(data, 42); }, m_data);
}
};
You have a couple of options:
If the types are conceptually the same type of thing (i.e. they have the same interface and could be substituted for one another), you can:
Create an abstract base type and use polymorphism to change which concrete type is instantiated
Change all places that use these types into templates, change which template(s) are instantiated at runtime, and then rely on compile-time "duck-typing" to do the rest
If the types are conceptually different (i.e. they should not have the same interface), you can use a union type (union, std::variant).
Take a look at std::variant.
A variant can hold a number of alternative types, like this:
std::variant<data_struct_1, data_struct_2> m;
The member m can contain either a data_struct_1 or a data_struct_2. The variant will remember which, so there's no need for you to carry that bool around anymore.
The interesting question is now, how do you treat m in your functions? After all, it could be either a data_struct_1 or a data_struct_2 in there. Instead of using branches for distinguishing these cases, variant uses visit: Whenever you encounter a point in the function that depends on the concrete type stored in m, you move that code into two overloaded functions: One function that handles the data_struct_1 case and another that handles the data_struct_2 case. The example on the visit cppreference page shows some possibilities for doing that with varying degrees of syntactic overhead.
The good thing about this approach is that it forces you to always handle both cases properly. You can't just go ahead and write an implementation for one case and then forget about the other, having it blow up later at runtime. It also encourages a style where you clearly separate common parts of your code from type-specific parts, which often results in a cleaner design in the long term.
One possibility is to use std::variant:
typedef std::variant<data_struct_1, data_struct_2> data_struct_1_or_2;
void foo(data_struct_1_or_2 ds) {
if (auto ds1 = std::get_if<data_struct_1>(&ds)) {
// use ds1, which is type data_struct_1*
} else if (auto ds2 = std::get_if<data_struct_2>(&ds)) {
// use d2, which is type data_struct_2*
} else {
throw std::logic_error("Unexpected value in variant");
}
}
void bar(data_struct_1_or_2 ds) {
foo(ds); // Don't need to use an if statement here
}
If you have a data_struct_1 or data_struct_2 and want to pass a reference to the object, rather than a copy, you could use pointers in the std::variant instead (although it'll get a bit confusing with std::get_if because you'll end up with a pointer to a pointer):
typedef std::variant<data_struct_1*, data_struct_2*> data_struct_1_or_2_ptr;
A simple C solution would be a union with a bool tag:
typedef struct {
struct head;
bool type_flag;
union{
struct data1 d1;
struct data2 d2
};
} data_struct;
std::variant is an overkill for this case IMO. Inheritance and runtime polymorphism is a waste of runtime and memory.
Regards,
FM.
I have tried union...
struct foo
{
union
{
struct // 2 bytes
{
char var0_1;
};
struct // 5 bytes
{
char var1_1;
int var1_2;
};
};
};
Problem: Unions do what I want, except they will always take the size of the biggest datatype. In my case I need struct foo to have some initialization that allows me to tell it which structure to chose of the two (if that is even legal) as shown below.
So after that, I tried class template overloading...
template <bool B>
class foo { }
template <>
class foo<true>
{
char var1;
}
template <>
class foo<false>
{
char var0;
int var1;
}
Problem: I was really happy with templates and the fact that I could use the same variable name on the char and int, but the problem was the syntax. Because the classes are created on compile-time, the template boolean variable needed to be a hardcoded constant, but in my case the boolean needs to be user-defined on runtime.
So I need something of the two "worlds." How can I achieve what I'm trying to do?
!!NOTE: The foo class/struct will later be inherited, therefore as already mentioned, size of foo is of utmost importance.
EDIT#1::
Application:
Basically this will be used to read/write (using a pointer as an interface) a specific data buffer and also allow me to create (new instance of the class/struct) the same data buffer. The variables you see above specify the length. If it's a smaller data buffer, the length is written in a char/byte. If it's a bigger data buffer, the first char/byte is null as a flag, and the int specifies the length instead. After the length it's obvious that the actual data follows, hence why the inheritance. Size of class is of the utmost importance. I need to have my cake and eat it too.
A layer of abstraction.
struct my_buffer_view{
std::size_t size()const{
if (!m_ptr)return 0;
if (*m_ptr)return *m_ptr;
return *reinterpret_cast<std::uint32_t const*>(m_ptr+1);
}
std::uint8_t const* data() const{
if(!m_ptr)return nullptr;
if(*m_ptr)return m_ptr+1;
return m_ptr+5;
}
std::uint8_t const* begin()const{return data();}
std::uint8_t const* end()const{return data()+size();}
my_buffer_view(std::uint_t const*ptr=nullptr):m_ptr(ptr){}
my_buffer_view(my_buffer_view const&)=default;
my_buffer_view& operator=(my_buffer_view const&)=default;
private:
std::uint8_t const* m_ptr=0;
};
No variable sized data anywhere. I coukd have used a union for size etx:
struct header{
std::uint8_t short_len;
union {
struct{
std::uint32_t long_len;
std::uint8_t long_buf[1];
}
struct {
std::short_buf[1];
}
} body;
};
but I just did pointer arithmetic instead.
Writing such a buffer to a bytestream is another problem entirely.
Your solution does not make sense. Think about your solution: you could define two independents classes: fooTrue and fooFalse with corresponding members exactly with the same result.
Probably, you are looking for a different solution as inheritance. For example, your fooTrue is baseFoo and your fooFalse is derivedFoo with as the previous one as base and extends it with another int member.
In this case, you have the polymorphism as the method to work in runtime.
You can't have your cake and eat it too.
The point of templates is that the specialisation happens at compile time. At run time, the size of the class is fixed (albeit, in an implementation-defined manner).
If you want the choice to be made at run time, then you can't use a mechanism that determines size at compile-time. You will need a mechanism that accommodates both possible needs. Practically, that means your base class will need to be large enough to contain all required members - which is essentially what is happening with your union based solution.
In reference to your "!!NOTE". What you are doing qualifies as premature optimisation. You are trying to optimise size of a base class without any evidence (e.g. measurement of memory usage) that the size difference is actually significant for your application (e.g. that it causes your application to exhaust available memory). The fact that something will be a base for a number of other classes is not sufficient, on its own, to worry about its size.
I'm aware of c++ templates, which allow you to write code for multiple types, but what if I want to store and access a type dynamically? Why is this so difficult to do in c++?
I would very much prefer to not have to do something like this:
enum SupportedTypes
{
IntType,
FloatType,
StringType
}
template <typename T>
class ClassThing
{
public:
T Value;
SupportedTypes Type;
}
...
//Not sure if you could even access thing->Type, but regardless, you get the idea...
switch (thing->Type)
{
case IntType:
DoSomething(((ClassThing<int>*)thing)->T);
break;
case FloatType:
DoSomething(((ClassThing<float>*)thing)->T);
break;
case StringType:
DoSomething(((ClassThing<string>*)thing)->T);
break;
}
Why doesn't c++ support something like this:
int whatIsThis = 5;
type t = typeid(whatIsThis); //typeid exists, but you can't do...:
t anotherInt = 5;
?
Another question that I have that I'm more optimistic of receiving a good answer to: if you choose to go the templated route, is there any way to maintain the type if you store it generically in a collection? E.g.:
vector<ClassThing> things;
(This will give an "argument list for class template ... is missing" error, by the way.) My guess is that no, this is not possible because the above is not possible.
How do I store and access a type dynamically in c++?
There are many options to pick from:
use runtime polymorphism, where you have a base class that might offer some common functionality and derived classes for each supported type; you often have to make some choices about how "fat" your interface should be (providing base class functions that only work meaningfully for a subset of derived types) vs. forcing the client to use dynamic_cast<> to recover/switch-on the runtime type
a particularly powerful technique is having the derived classes be type-specific instantiations of the same template, as it means you can support arbitrary types parametrically, i.e. if they provide the semantics of usage that the template expects
use a discriminated union (basically, a type identification enum/int alongside a union of the supported types) - std::variant<> is a good choice for this
when creating/storing a value capture you'll necessarily know it's type
you can record both its typeinfo and address, then when accessing the variable later you can use the typeinfo to test whether the object is of a specific type - trying each supported type until a match is found - std::any<> is a good choice for this, or
you can capture an arbitrary set of type-specific operations using function pointers or std::function<>
Why doesn't c++ support something like this:
int whatIsThis = 5;
type t = typeid(whatIsThis); //typeid exists, but you can't do...:
t anotherInt = 5;?
It does, with decltype and auto:
int whatIsThis = 5;
using t = decltype(whatIsThis);
t anotherInt = 5;
auto anotherWhatever = whatIsThis; // another way to create an additional
// variable of the same type
For runtime polymorphism, you might actually want to read up on factories (which create one of many types of object - all derived from some base interface - given some runtime input), and clone functions (which create a copy of a variable of unknown runtime type).
if you choose to go the templated route, is there any way to maintain the type if you store it generically in a collection: vector<ClassThing> things; (This will give an "argument list for class template ... is missing" error, by the way.)
You can't create even a single object from a template without instantiating it, so no there's no way to have an entire vector either. A reasonable approach is to derive the template from a base class and store [smart] pointers or std::reference_wrappers to the base class in the vector.
int x = 5;
decltype(x) y = 4;
auto z = 3;
decltype(a) will give you the type of a. You can then use typedef to store the types, or other functions to remove references from the type if necessary.
For example:
typedef decltype(a) type1;
type1 b = 2 * a;
auto makes you not need to specify the type at all.
The only thing you need is to compile in c++11 mode (-std=c++11) or later.
As for the vector question, decltype will work there too.
I won't steal the answer, but I will provide the method I ended up using for those who are trying to do something similar. (I am writing my own raw serialization and deserialization code with memcpy.) What I had hoped to do was store and maintain various arrangements of types without having to create a bunch of structs or classes, e.g. (from my question):
template <typename T>
class ClassThing
{
public:
T Value;
SupportedTypes Type;
}
//Then store everything in a:
vector<ClassThing> things;
However, attempting to store a templated class in a vector will give an "argument list for class template ... is missing" error, because as Tony D said in his answer, "You can't create even a single object from a template without instantiating it..." I also didn't want to use any external libraries like boost (for variants).
So, I concluded that because I absolutely wanted to use a single collection to store all of the structures, I simply could not use a templated class. Instead, I resolved to use a templated constructor (only) and a void* for the Value, as well as store the type's hash and the number of bytes required for storing/copying the type:
class ClassThing
{
public:
void* Value;
unsigned long long TypeHash;
unsigned long long NumberOfBytes;
template <typename T>
ClassThing(T passedValue)
{
Value = &passedValue;
TypeHash = typeid(passedValue).hash_code();
NumberOfBytes = sizeof(T);
}
//For strings, do this:
ClassThing(const char* passedValue, unsigned short passedNumberOfBytes)
{
Value = const_cast<char*>(passedValue);
TypeHash = typeid(char*).hash_code();
NumberOfBytes = length;
}
}
Unfortunately, this solution loses the type, but since the serialization and deserialization process I'm using is a simple memcpy, all I needed was a pointer to the data and the number of bytes it used. The reason I store the type's hash here is so that I can perform type checking before serialization (e.g. make sure a float isn't being serialized where an int should be).
For the deserialization process, I will be using this technique: https://stackoverflow.com/a/15313677/1599699
Since I do not know the type, I will simply have to expect that the cast from void* matches up with the serialization process, although I can at least check the NumberOfBytes value and ideally the TypeHash as well, if those are available. On the deserialization end, I will end up with a void* and do this:
void* deserializedData = ...;
float deserializedFloat = *(float*)&deserializedData;
This of course is not the ideal solution to my problem, but it allows me to do what I want, which is extremely high performance serialization and deserialization to binary with low memory usage and extremely low maintenance.
Hope this helps someone!
Although this is not exactly a C++ answer (rather, a C one), it should be valid in C++ all the same.
The type void* is a pointer to untyped memory. Basically, you can cast it to any type of pointer, then dereference. Example:
int x1 = 42;
long l1 = 123456789L;
void* test = &x1;
int x2 = *(int*)test; // x2 now contains the contents of x1
test = &l1;
long l2 = *(long*)test; // l2 now contains the contents of l1
This is in no way the most delicate way of solving your problem, but it is an option.
Further reading:
https://www.astro.umd.edu/~dcr/Courses/ASTR615/intro_C/node15.html
http://www.circuitstoday.com/void-pointers-in-c
http://www.nongnu.org/c-prog-book/online/x658.html
If you want dynamic types (in C++11 or better, e.g. C++14) you could make a variant type by making a class with some union:
class Thing {
enum SupportedTypes type;
union {
intptr_t num; // when type == IntType
double flo; // when type == FloatType
std::string str; // when type == StringType
}
// etc....
};
Be careful, you need to obey to the rule of five and you probably should explicitly call the destructor of std::string on str when type == StringType, etc...
Some third party libraries might be helpful: Boost variants, Qt QVariant, etc...
I am trying to copy part of a large structure and I was hoping I could use pointer arithmetic to copy chunks of it at a time. So if I have the following stucture
struct {
int field1;
char field2;
myClass field3;
.
.
.
myOtherClass field42;
} myStruct;
struct origionalStruct;
struct *pCopyStruct;
can I use memcpy() to copy part of it using pointer arithmetic?
memcpy(pCopyStruct, &origionalStruct.field1,
(char*)&origionalStuct.field1 - (char*)&origionalStuct.field23);
I know that pointer arithmetic is only valid for arrays, but I was hoping I could get around that by casting everything to (char*).
My answer only holds for c++.
Using memcpy() to copy member variables of objects breaks encapsulation and is not good practice in general. I.e. only do that if you have very good reason. It can work if you are careful, but you are making your program very brittle: You increase the risk that future changes will introduce bugs.
E.g. also see http://flylib.com/books/en/2.123.1.431/1/
It would be best to put the fields you want to copy into a nested struct and just assign that to the corresponding field of the new struct. That would avoid writing, increases greatly readability and - least not last - maintains type-safety. All which memcpy does not provide.
offsetof() or using the addresses of enclosing fields would obviously not work if the copied fields are at the end or beginning of the struct.
struct {
int field1;
struct { char fields } cpy_fields;
} a, b;
a.cpy_fields = b.cpy_fields;
When using gcc, you can enable plan9-extensions and use an anonymous struct, but need a typedef for the inner:
typedef struct { char field1; } Inner;
struct {
int field1;
Inner;
} a, b;
This does not change existing code which can do: a.field2. You can still access the struct as a whole by its typename (provided you only have one instance in the outer struct): a.Inner = b.Inner.
While the first part (anonymous struct) is standard since C99, the latter is part of the plan9-extensions (which are very interesting for its other feature, too). Actually the other feature might provide an even better sulution for your problem. You might have a look at the doc-page and let it settle for a sec or two to get the implications. Still wonder why this feature did not make it into the standard (no extra code, more type-safety as much less casts required).
Yes, you can do:
memcpy(pCopyStruct, &origionalStruct.field1,
(char*)&origionalStuct.field23 - (char*)&origionalStuct.field1);
However, you probably want to use the offsetof() macro found in stddef.h.
memcpy(pCopyStruct, &originalStruct.field1,
offsetof(struct myStruct, field23)-offsetof(struct myStruct, field1));
Where if field1 is at offset 0 (first in the struct) you can omit the subtraction.
For completeness, the offsetof macro can be defined as:
#define offsetof(st, m) ((size_t)(&((st *)0)->m))
Instead of having to remember to initialize a simple 'C' structure, I might derive from it and zero it in the constructor like this:
struct MY_STRUCT
{
int n1;
int n2;
};
class CMyStruct : public MY_STRUCT
{
public:
CMyStruct()
{
memset(this, 0, sizeof(MY_STRUCT));
}
};
This trick is often used to initialize Win32 structures and can sometimes set the ubiquitous cbSize member.
Now, as long as there isn't a virtual function table for the memset call to destroy, is this a safe practice?
You can simply value-initialize the base, and all its members will be zero'ed out. This is guaranteed
struct MY_STRUCT
{
int n1;
int n2;
};
class CMyStruct : public MY_STRUCT
{
public:
CMyStruct():MY_STRUCT() { }
};
For this to work, there should be no user declared constructor in the base class, like in your example.
No nasty memset for that. It's not guaranteed that memset works in your code, even though it should work in practice.
PREAMBLE:
While my answer is still Ok, I find litb's answer quite superior to mine because:
It teaches me a trick that I did not know (litb's answers usually have this effect, but this is the first time I write it down)
It answers exactly the question (that is, initializing the original struct's part to zero)
So please, consider litb's answer before mine. In fact, I suggest the question's author to consider litb's answer as the right one.
Original answer
Putting a true object (i.e. std::string) etc. inside will break, because the true object will be initialized before the memset, and then, overwritten by zeroes.
Using the initialization list doesn't work for g++ (I'm surprised...). Initialize it instead in the CMyStruct constructor body. It will be C++ friendly:
class CMyStruct : public MY_STRUCT
{
public:
CMyStruct() { n1 = 0 ; n2 = 0 ; }
};
P.S.: I assumed you did have no control over MY_STRUCT, of course. With control, you would have added the constructor directly inside MY_STRUCT and forgotten about inheritance. Note that you can add non-virtual methods to a C-like struct, and still have it behave as a struct.
EDIT: Added missing parenthesis, after Lou Franco's comment. Thanks!
EDIT 2 : I tried the code on g++, and for some reason, using the initialization list does not work. I corrected the code using the body constructor. The solution is still valid, though.
Please reevaluate my post, as the original code was changed (see changelog for more info).
EDIT 3 : After reading Rob's comment, I guess he has a point worthy of discussion: "Agreed, but this could be an enormous Win32 structure which may change with a new SDK, so a memset is future proof."
I disagree: Knowing Microsoft, it won't change because of their need for perfect backward compatibility. They will create instead an extended MY_STRUCTEx struct with the same initial layout as MY_STRUCT, with additionnal members at the end, and recognizable through a "size" member variable like the struct used for a RegisterWindow, IIRC.
So the only valid point remaining from Rob's comment is the "enormous" struct. In this case, perhaps a memset is more convenient, but you will have to make MY_STRUCT a variable member of CMyStruct instead of inheriting from it.
I see another hack, but I guess this would break because of possible struct alignment problem.
EDIT 4: Please take a look at Frank Krueger's solution. I can't promise it's portable (I guess it is), but it is still interesting from a technical viewpoint because it shows one case where, in C++, the "this" pointer "address" moves from its base class to its inherited class.
Much better than a memset, you can use this little trick instead:
MY_STRUCT foo = { 0 };
This will initialize all members to 0 (or their default value iirc), no need to specifiy a value for each.
This would make me feel much safer as it should work even if there is a vtable (or the compiler will scream).
memset(static_cast<MY_STRUCT*>(this), 0, sizeof(MY_STRUCT));
I'm sure your solution will work, but I doubt there are any guarantees to be made when mixing memset and classes.
This is a perfect example of porting a C idiom to C++ (and why it might not always work...)
The problem you will have with using memset is that in C++, a struct and a class are exactly the same thing except that by default, a struct has public visibility and a class has private visibility.
Thus, what if later on, some well meaning programmer changes MY_STRUCT like so:
struct MY_STRUCT
{
int n1;
int n2;
// Provide a default implementation...
virtual int add() {return n1 + n2;}
};
By adding that single function, your memset might now cause havoc.
There is a detailed discussion in comp.lang.c+
The examples have "unspecified behaviour".
For a non-POD, the order by which the compiler lays out an object (all bases classes and members) is unspecified (ISO C++ 10/3). Consider the following:
struct A {
int i;
};
class B : public A { // 'B' is not a POD
public:
B ();
private:
int j;
};
This can be laid out as:
[ int i ][ int j ]
Or as:
[ int j ][ int i ]
Therefore, using memset directly on the address of 'this' is very much unspecified behaviour. One of the answers above, at first glance looks to be safer:
memset(static_cast<MY_STRUCT*>(this), 0, sizeof(MY_STRUCT));
I believe, however, that strictly speaking this too results in unspecified behaviour. I cannot find the normative text, however the note in 10/5 says: "A base class subobject may have a layout (3.7) different from the layout of a most derived object of the same type".
As a result, I compiler could perform space optimizations with the different members:
struct A {
char c1;
};
struct B {
char c2;
char c3;
char c4;
int i;
};
class C : public A, public B
{
public:
C ()
: c1 (10);
{
memset(static_cast<B*>(this), 0, sizeof(B));
}
};
Can be laid out as:
[ char c1 ] [ char c2, char c3, char c4, int i ]
On a 32 bit system, due to alighments etc. for 'B', sizeof(B) will most likely be 8 bytes. However, sizeof(C) can also be '8' bytes if the compiler packs the data members. Therefore the call to memset might overwrite the value given to 'c1'.
Precise layout of a class or structure is not guaranteed in C++, which is why you should not make assumptions about the size of it from the outside (that means if you're not a compiler).
Probably it works, until you find a compiler on which it doesn't, or you throw some vtable into the mix.
If you already have a constructor, why not just initialize it there with n1=0; n2=0; -- that's certainly the more normal way.
Edit: Actually, as paercebal has shown, ctor initialization is even better.
My opinion is no. I'm not sure what it gains either.
As your definition of CMyStruct changes and you add/delete members, this can lead to bugs. Easily.
Create a constructor for CMyStruct that takes a MyStruct has a parameter.
CMyStruct::CMyStruct(MyStruct &)
Or something of that sought. You can then initialize a public or private 'MyStruct' member.
From an ISO C++ viewpoint, there are two issues:
(1) Is the object a POD? The acronym stands for Plain Old Data, and the standard enumerates what you can't have in a POD (Wikipedia has a good summary). If it's not a POD, you can't memset it.
(2) Are there members for which all-bits-zero is invalid ? On Windows and Unix, the NULL pointer is all bits zero; it need not be. Floating point 0 has all bits zero in IEEE754, which is quite common, and on x86.
Frank Kruegers tip addresses your concerns by restricting the memset to the POD base of the non-POD class.
Try this - overload new.
EDIT: I should add - This is safe because the memory is zeroed before any constructors are called. Big flaw - only works if object is dynamically allocated.
struct MY_STRUCT
{
int n1;
int n2;
};
class CMyStruct : public MY_STRUCT
{
public:
CMyStruct()
{
// whatever
}
void* new(size_t size)
{
// dangerous
return memset(malloc(size),0,size);
// better
if (void *p = malloc(size))
{
return (memset(p, 0, size));
}
else
{
throw bad_alloc();
}
}
void delete(void *p, size_t size)
{
free(p);
}
};
If MY_STRUCT is your code, and you are happy using a C++ compiler, you can put the constructor there without wrapping in a class:
struct MY_STRUCT
{
int n1;
int n2;
MY_STRUCT(): n1(0), n2(0) {}
};
I'm not sure about efficiency, but I hate doing tricks when you haven't proved efficiency is needed.
Comment on litb's answer (seems I'm not yet allowed to comment directly):
Even with this nice C++-style solution you have to be very careful that you don't apply this naively to a struct containing a non-POD member.
Some compilers then don't initialize correctly anymore.
See this answer to a similar question.
I personally had the bad experience on VC2008 with an additional std::string.
What I do is use aggregate initialization, but only specifying initializers for members I care about, e.g:
STARTUPINFO si = {
sizeof si, /*cb*/
0, /*lpReserved*/
0, /*lpDesktop*/
"my window" /*lpTitle*/
};
The remaining members will be initialized to zeros of the appropriate type (as in Drealmer's post). Here, you are trusting Microsoft not to gratuitously break compatibility by adding new structure members in the middle (a reasonable assumption). This solution strikes me as optimal - one statement, no classes, no memset, no assumptions about the internal representation of floating point zero or null pointers.
I think the hacks involving inheritance are horrible style. Public inheritance means IS-A to most readers. Note also that you're inheriting from a class which isn't designed to be a base. As there's no virtual destructor, clients who delete a derived class instance through a pointer to base will invoke undefined behaviour.
I assume the structure is provided to you and cannot be modified. If you can change the structure, then the obvious solution is adding a constructor.
Don't over engineer your code with C++ wrappers when all you want is a simple macro to initialise your structure.
#include <stdio.h>
#define MY_STRUCT(x) MY_STRUCT x = {0}
struct MY_STRUCT
{
int n1;
int n2;
};
int main(int argc, char *argv[])
{
MY_STRUCT(s);
printf("n1(%d),n2(%d)\n", s.n1, s.n2);
return 0;
}
It's a bit of code, but it's reusable; include it once and it should work for any POD. You can pass an instance of this class to any function expecting a MY_STRUCT, or use the GetPointer function to pass it into a function that will modify the structure.
template <typename STR>
class CStructWrapper
{
private:
STR MyStruct;
public:
CStructWrapper() { STR temp = {}; MyStruct = temp;}
CStructWrapper(const STR &myStruct) : MyStruct(myStruct) {}
operator STR &() { return MyStruct; }
operator const STR &() const { return MyStruct; }
STR *GetPointer() { return &MyStruct; }
};
CStructWrapper<MY_STRUCT> myStruct;
CStructWrapper<ANOTHER_STRUCT> anotherStruct;
This way, you don't have to worry about whether NULLs are all 0, or floating point representations. As long as STR is a simple aggregate type, things will work. When STR is not a simple aggregate type, you'll get a compile-time error, so you won't have to worry about accidentally misusing this. Also, if the type contains something more complex, as long as it has a default constructor, you're ok:
struct MY_STRUCT2
{
int n1;
std::string s1;
};
CStructWrapper<MY_STRUCT2> myStruct2; // n1 is set to 0, s1 is set to "";
On the downside, it's slower since you're making an extra temporary copy, and the compiler will assign each member to 0 individually, instead of one memset.