Related
This question already has answers here:
Designated initializers in C++20
(2 answers)
Closed 4 months ago.
Is it possible to initialize structs in C++ as indicated below:
struct address {
int street_no;
char *street_name;
char *city;
char *prov;
char *postal_code;
};
address temp_address = { .city = "Hamilton", .prov = "Ontario" };
The links here and here mention that it is possible to use this style only in C. If so why is this not possible in C++? Is there any underlying technical reason why it is not implemented in C++, or is it bad practice to use this style. I like using this way of initializing because my struct is big and this style gives me clear readability of what value is assigned to which member.
Please share with me if there are other ways through which we can achieve the same readability.
I have referred the following links before posting this question:
C/C++ for AIX
C Structure Initialization with Variable
Static structure initialization with tags in C++
C++11 Proper Structure Initialization
If you want to make it clear what each initializer value is, just split it up on multiple lines, with a comment on each:
address temp_addres = {
0, // street_no
nullptr, // street_name
"Hamilton", // city
"Ontario", // prov
nullptr, // postal_code
};
After my question resulted in no satisfying result (because C++ doesn't implement tag-based init for structures), I took the trick I found here: Are members of a C++ struct initialized to 0 by default?
For you it would amount to do that:
address temp_address = {}; // will zero all fields in C++
temp_address.city = "Hamilton";
temp_address.prov = "Ontario";
This is certainly the closest to what you wanted originally (zero all the fields except those you want to initialize).
As others have mentioned this is designated initializer.
This feature is part of C++20
The field identifiers are indeed C initializer syntax. In C++ just give the values in the correct order without the field names. Unfortunately this means you need to give them all (actually you can omit trailing zero-valued fields and the result will be the same):
address temp_address = { 0, 0, "Hamilton", "Ontario", 0 };
This feature is called designated initializers. It is an addition to the C99 standard. However, this feature was left out of the C++11. According to The C++ Programming Language, 4th edition, Section 44.3.3.2 (C Features Not Adopted by C++):
A few additions to C99 (compared with C89) were deliberately not adopted in C++:
[1] Variable-length arrays (VLAs); use vector or some form of dynamic array
[2] Designated initializers; use constructors
The C99 grammar has the designated initializers [See ISO/IEC 9899:2011, N1570 Committee Draft - April 12, 2011]
6.7.9 Initialization
initializer:
assignment-expression
{ initializer-list }
{ initializer-list , }
initializer-list:
designation_opt initializer
initializer-list , designationopt initializer
designation:
designator-list =
designator-list:
designator
designator-list designator
designator:
[ constant-expression ]
. identifier
On the other hand, the C++11 does not have the designated initializers [See ISO/IEC 14882:2011, N3690 Committee Draft - May 15, 2013]
8.5 Initializers
initializer:
brace-or-equal-initializer
( expression-list )
brace-or-equal-initializer:
= initializer-clause
braced-init-list
initializer-clause:
assignment-expression
braced-init-list
initializer-list:
initializer-clause ...opt
initializer-list , initializer-clause ...opt
braced-init-list:
{ initializer-list ,opt }
{ }
In order to achieve the same effect, use constructors or initializer lists:
I know this question is quite old, but I found another way of initializing, using constexpr and currying:
struct mp_struct_t {
public:
constexpr mp_struct_t(int member1) : mp_struct_t(member1, 0, 0) {}
constexpr mp_struct_t(int member1, int member2, int member3) : member1(member1), member2(member2), member3(member3) {}
constexpr mp_struct_t another_member(int member) { return {member1, member, member3}; }
constexpr mp_struct_t yet_another_one(int member) { return {member1, member2, member}; }
int member1, member2, member3;
};
static mp_struct_t a_struct = mp_struct_t{1}
.another_member(2)
.yet_another_one(3);
This method also works for global static variables and even constexpr ones.
The only disadvantage is the bad maintainability: Everytime another member has to be made initializable using this method, all member initialization methods have to be changed.
You can just initialize via a constructor:
struct address {
address() : city("Hamilton"), prov("Ontario") {}
int street_no;
char *street_name;
char *city;
char *prov;
char *postal_code;
};
I might be missing something here, by why not:
#include <cstdio>
struct Group {
int x;
int y;
const char* s;
};
int main()
{
Group group {
.x = 1,
.y = 2,
.s = "Hello it works"
};
printf("%d, %d, %s", group.x, group.y, group.s);
}
You can even pack Gui13's solution into single initialization statement:
struct address {
int street_no;
char *street_name;
char *city;
char *prov;
char *postal_code;
};
address ta = (ta = address(), ta.city = "Hamilton", ta.prov = "Ontario", ta);
Disclaimer: I don't recommend this style
It's not implemented in C++. (also, char* strings? I hope not).
Usually if you have so many parameters it is a fairly serious code smell. But instead, why not simply value-initialize the struct and then assign each member?
Inspired by this really neat answer: (https://stackoverflow.com/a/49572324/4808079)
You can do lamba closures:
// Nobody wants to remember the order of these things
struct SomeBigStruct {
int min = 1;
int mean = 3 ;
int mode = 5;
int max = 10;
string name;
string nickname;
... // the list goes on
}
.
class SomeClass {
static const inline SomeBigStruct voiceAmps = []{
ModulationTarget $ {};
$.min = 0;
$.nickname = "Bobby";
$.bloodtype = "O-";
return $;
}();
}
Or, if you want to be very fancy
#define DesignatedInit(T, ...)\
[]{ T ${}; __VA_ARGS__; return $; }()
class SomeClass {
static const inline SomeBigStruct voiceAmps = DesignatedInit(
ModulationTarget,
$.min = 0,
$.nickname = "Bobby",
$.bloodtype = "O-",
);
}
There are some drawbacks involved with this, mostly having to do with uninitialized members. From what the linked answers comments say, it compiles efficiently, though I have not tested it.
Overall, I just think it's a neat approach.
I found this way of doing it for global variables, that does not require to modify the original structure definition :
struct address {
int street_no;
char *street_name;
char *city;
char *prov;
char *postal_code;
};
then declare the variable of a new type inherited from the original struct type and use the constructor for fields initialisation :
struct temp_address : address { temp_address() {
city = "Hamilton";
prov = "Ontario";
} } temp_address;
Not quite as elegant as the C style though ...
For a local variable it requires an additional memset(this, 0, sizeof(*this)) at the beginning of the constructor, so it's clearly not worse it and #gui13 's answer is more appropriate.
(Note that 'temp_address' is a variable of type 'temp_address', however this new type inherit from 'address' and can be used in every place where 'address' is expected, so it's OK.)
In C++ the C-style initializers were replaced by constructors which by compile time can ensure that only valid initializations are performed (i.e. after initialization the object members are consistent).
It is a good practice, but sometimes a pre-initialization is handy, like in your example. OOP solves this by abstract classes or creational design patterns.
In my opinion, using this secure way kills the simplicity and sometimes the security trade-off might be too expensive, since simple code does not need sophisticated design to stay maintainable.
As an alternative solution, I suggest to define macros using lambdas to simplify the initialization to look almost like C-style:
struct address {
int street_no;
const char *street_name;
const char *city;
const char *prov;
const char *postal_code;
};
#define ADDRESS_OPEN [] { address _={};
#define ADDRESS_CLOSE ; return _; }()
#define ADDRESS(x) ADDRESS_OPEN x ADDRESS_CLOSE
The ADDRESS macro expands to
[] { address _={}; /* definition... */ ; return _; }()
which creates and calls the lambda. Macro parameters are also comma separated, so you need to put the initializer into brackets and call like
address temp_address = ADDRESS(( _.city = "Hamilton", _.prov = "Ontario" ));
You could also write generalized macro initializer
#define INIT_OPEN(type) [] { type _={};
#define INIT_CLOSE ; return _; }()
#define INIT(type,x) INIT_OPEN(type) x INIT_CLOSE
but then the call is slightly less beautiful
address temp_address = INIT(address,( _.city = "Hamilton", _.prov = "Ontario" ));
however you can define the ADDRESS macro using general INIT macro easily
#define ADDRESS(x) INIT(address,x)
In GNUC++ (seems to be obsolete since 2.5, a long time ago :) See the answers here: C struct initialization using labels. It works, but how?), it is possible to initialize a struct like this:
struct inventory_item {
int bananas;
int apples;
int pineapples;
};
inventory_item first_item = {
bananas: 2,
apples: 49,
pineapples: 4
};
You have
The standard initialization list
address temp_address {
/* street_no */,
/* street_name */,
...
/* postal_code */
};
address temp_address2 = {
/* street_no */,
/* street_name */,
...
/* postal_code */
}
The dot notation
address temp_address;
temp_address.street_no = ...;
temp_address.street_name = ...;
...
temp_address.postal_code = ...;
The designated aggregate initialization, where the initialization list contains that labels of each member of the structure (see documentation) available from C++20 onward.
Treating a struct like a C++ class - in C++ structures are actually special types of classes, where all members are public (unlike a standard C++ class where all members are private if not specified otherwise explicitly) as well as that when using inheritance they default to public:
struct Address {
int street_no;
...
char* postal_code;
Address (int _street_no, ... , char* _postal_code)
: street_no(_street_no),
...
postal_code(_postal_code)
{}
}
...
Address temp_address ( /* street_no */, ..., /* postal_code */);
When it comes to the way you initialize your structure you should consider the following aspects:
Portability - different compilers, different degree of C++ standard completeness and different C++ standards altogether do limit your options. If you have to work with let's say a C++11 compiler but want to use the C++20 designated aggregate initialization you are out of luck
Readability - what is more readable: temp_address.city = "Toronto" or temp_address { ..., "Toronto", ... }? Readability of your code is very important. Especially when you have large structures (worse - nested ones), having unlabeled values all over the place is just asking for trouble
Scalability - anything that depends on a specific order is not a good idea. The same goes for lack of labels. You want to move a member up or down the address space of the structure? Good luck with an unlabeled initialization list (hunting down swapped values in structure initialization is a nightmare)... You want to add a new member? Again good luck with anything that depends on a specific order.
While the dot notation means you type more the benefits you get from using it outweigh this issue and as such I can recommend it unless you have a small structure that is future-proof in terms of lack of changes in its structure, in which case you can afford to go with an initialization list. Remember: whenever working with other people writing code that is easy to follow is essential.
I faced a similar problem today, where I have a struct that I want to fill with test data which will be passed as arguments to a function I'm testing. I wanted to have a vector of these structs and was looking for a one-liner method to initialize each struct.
I ended up going with a constructor function in the struct, which I believe was also suggested in a few answers to your question.
It's probably bad practice to have the arguments to the constructor have the same names as the public member variables, requiring use of the this pointer. Someone can suggest an edit if there is a better way.
typedef struct testdatum_s {
public:
std::string argument1;
std::string argument2;
std::string argument3;
std::string argument4;
int count;
testdatum_s (
std::string argument1,
std::string argument2,
std::string argument3,
std::string argument4,
int count)
{
this->rotation = argument1;
this->tstamp = argument2;
this->auth = argument3;
this->answer = argument4;
this->count = count;
}
} testdatum;
Which I used in in my test function to call the function being tested with various arguments like this:
std::vector<testdatum> testdata;
testdata.push_back(testdatum("val11", "val12", "val13", "val14", 5));
testdata.push_back(testdatum("val21", "val22", "val23", "val24", 1));
testdata.push_back(testdatum("val31", "val32", "val33", "val34", 7));
for (std::vector<testdatum>::iterator i = testdata.begin(); i != testdata.end(); ++i) {
function_in_test(i->argument1, i->argument2, i->argument3, i->argument4m i->count);
}
It is possible, but only if the struct you're initializing is a POD (plain old data) struct. It cannot contain any methods, constructors, or even default values.
Everything works in Visual Studio 2017, but I get linker errors in GCC (6.5.0).
Here is some sample code that isolates my problem:
#include <iostream>
struct Foo{
static constexpr const char* s[] = {"one","two","three"};
};
int main(){
std::cout << Foo::s[0] << std::endl; //this works in both compilers
const char* const* str_ptr = nullptr;
str_ptr = Foo::s; //LINKER ERROR in GCC; works in VS
std::cout << str_ptr[1] << std::endl; //works in VS
return 0;
}
In GCC, I get undefined reference to 'Foo::s'. I need the initialization of Foo::s to stay in the declaration of the struct, which is why I used constexpr. Is there a way to reference Foo::s dynamically, i.e. with a pointer?
More Background Info
I'll now explain why I want to do this. I am developing embedded software to control configuration of a device. The software loads config files that contain the name of a parameter and its value. The set of parameters being configured is determined at compile-time, but needs to be modular so that it's easy to add new parameters and expand them as development of the device continues. In other words, their definition needs to be in one single place in the code base.
My actual code base is thousands of lines and works in Visual Studio, but here is a boiled-down toy example:
#include <iostream>
#include <string>
#include <vector>
//A struct for an arbitrary parameter
struct Parameter {
std::string paramName;
int max_value;
int value;
const char* const* str_ptr = nullptr;
};
//Structure of parameters - MUST BE DEFINED IN ONE PLACE
struct Param_FavoriteIceCream {
static constexpr const char* n = "FavoriteIceCream";
enum { vanilla, chocolate, strawberry, NUM_MAX };
static constexpr const char* s[] = { "vanilla","chocolate","strawberry" };
};
struct Param_FavoriteFruit {
static constexpr const char* n = "FavoriteFruit";
enum { apple, banana, grape, mango, peach, NUM_MAX };
static constexpr const char* s[] = { "apple","banana","grape","mango","peach" };
};
int main() {
//Set of parameters - determined at compile-time
std::vector<Parameter> params;
params.resize(2);
//Configure these parameters objects - determined at compile-time
params[0].paramName = Param_FavoriteIceCream::n;
params[0].max_value = Param_FavoriteIceCream::NUM_MAX;
params[0].str_ptr = Param_FavoriteIceCream::s; //!!!! LINKER ERROR IN GCC !!!!!!
params[1].paramName = Param_FavoriteFruit::n;
params[1].max_value = Param_FavoriteFruit::NUM_MAX;
params[1].str_ptr = Param_FavoriteFruit::s; //!!!! LINKER ERROR IN GCC !!!!!!
//Set values by parsing files - determined at run-time
std::string param_string = "FavoriteFruit"; //this would be loaded from a file
std::string param_value = "grape"; //this would be loaded from a file
for (size_t i = 0; i < params.size(); i++) {
for (size_t j = 0; j < params[i].max_value; j++) {
if (params[i].paramName == param_string
&& params[i].str_ptr[j] == param_value) {
params[i].value = j;
break;
}
}
}
return 0;
}
As you can see, there are enums and string arrays involved and these need to match, so for maintenance purposes I need to keep these in the same place. Furthermore, since this code is already written and will be used in both Windows and Linux environments, the smaller the fix, the better. I would prefer not to have to re-write thousands of lines just to get it to compile in Linux. Thanks!
The program is valid in C++17. The program is not valid in C++14 or older standards. The default standard mode of GCC 6.5.0 is C++14.
To make the program conform to C++14, you must define the static member (in exactly one translation unit). Since C++17, the constexpr declaration is implicitly an inline variable definition and thus no separate definition is required.
Solution 1: Upgrade your compiler and use C++17 standard (or later if you're from the future), which has inline variables. Inline variables have been implemented since GCC 7.
Solution 2: Define the variable outside the class definition in exactly one translation unit (the initialisation remains in the declaration).
It looks like you are not using C++17, and pre-C++17 this is undefined behavior, and one that I really dislike. You do not have a definition for your s, but you are ODR-using it, which means that you need to have a definition.
To define this, you have to define it in .cpp file.
In C++17 that would be the valid code. I am not familiar with MSVC, so I am not sure why it works fine there - be it that it is compiled as C++17, or because it is just a different manifestation of undefined behavior.
For C++98, C++11, and C++14 you need to explicitly tell the compiler where the initialization of Foo::s lives (see below) and you are good to go.
struct Foo{
static const char* s[];
};
const char* Foo::s[] = {"one","two","three"};
And as explained in one of the comments your initialization is OK from C++17.
I'm trying to implement a minheap in C++. However the following code keeps eliciting errors such as :
heap.cpp:24:4: error: cannot convert 'complex int' to 'int' in assignment
l=2i;
^
heap.cpp:25:4: error: cannot convert 'complex int' to 'int' in assignment
r=2i+1;
^
heap.cpp: In member function 'int Heap::main()':
heap.cpp:47:16: error: no matching function for call to 'Heap::heapify(int [11], int&)'
heapify(a,i);
^
heap.cpp:47:16: note: candidate is:
heap.cpp:21:5: note: int Heap::heapify(int)
int heapify(int i) //i is the parent index, a[] is the heap array
^
heap.cpp:21:5: note: candidate expects 1 argument, 2 provided
make: * [heap] Error 1
#include <iostream>
using namespace std;
#define HEAPSIZE 10
class Heap
{
int a[HEAPSIZE+1];
Heap()
{
for (j=1;j<(HEAPISZE+1);j++)
{
cin>>a[j];
cout<<"\n";
}
}
int heapify(int i) //i is the parent index, a[] is the heap array
{
int l,r,smallest,temp;
l=2i;
r=2i+1;
if (l<11 && a[l]<a[i])
smallest=l;
else
smallest=i;
if (r<11 && a[r]<a[smallest])
smallest=r;
if (smallest != i)
{
temp = a[smallest];
a[smallest] = a[i];
a[i]=temp;
heapify(smallest);
}
}
int main()
{
int i;
for (i=1;i<=HEAPSIZE;i++)
{
heapify(a,i);
}
}
}
Ultimately, the problem with this code is that it was written by someone who skipped chapters 1, 2 and 3 of "C++ for Beginners". Lets start with some basics.
#include <iostream>
using namespace std;
#define HEAPSIZE 10
Here, we have included the C++ header for I/O (input output). A fine start. Then, we have issued a directive that says "Put everything that is in namespace std into the global namespace". This saves you some typing, but means that all of the thousands of things that were carefully compartmentalized into std:: can now conflict with names you want to use in your code. This is A Bad Thing(TM). Try to avoid doing it.
Then we went ahead and used a C-ism, a #define. There are times when you'll still need to do this in C++, but it's better to avoid it. We'll come back to this.
The next problem, at least in the code you posted, is a misunderstanding of the C++ class.
The 'C' language that C++ is based on has the concept of a struct for describing a collection of data items.
struct
{
int id;
char name[64];
double wage;
};
It's important to notice the syntax - the trailing ';'. This is because you can describe a struct and declare variables of it's type at the same time.
struct { int id; char name[64]; } earner, manager, ceo;
This declares a struct, which has no type name, and variables earner, manager and ceo of that type. The semicolon tells the compiler when we're done with this statement. Learning when you need a semicolon after a '}' takes a little while; usually you don't, but in struct/class definition you do.
C++ added lots of things to C, but one common misunderstanding is that struct and class are somehow radically different.
C++ originally extended the struct concept by allowing you to describe functions in the context of the struct and by allowing you to describe members/functions as private, protected or public, and allowing inheritance.
When you declare a struct, it defaults to public. A class is nothing more than a struct which starts out `private.
struct
{
int id;
char name[64];
double wage;
};
class
{
public:
int id;
char name[64];
double wage;
};
The resulting definitions are both identical.
Your code does not have an access specifier, so everything in your Heap class is private. The first and most problematic issue this causes is: Nobody can call ANY of your functions, because they are private, they can only be called from other class members. That includes the constructor.
class Foo { Foo () {} };
int main()
{
Foo f;
return 0;
}
The above code will fail to compile, because main is not a member of Foo and thus cannot call anything private.
This brings us to another problem. In your code, as posted, main is a member of Foo. The entry point of a C++ program is main, not Foo::main or std::main or Foo::bar::herp::main. Just, good old int main(int argc, const char* argv[]) or int main().
In C, with structs, because C doesn't have member functions, you would never be in a case where you were using struct-members directly without prefixing that with a pointer or member reference, e.g. foo.id or ptr->wage. In C++, in a member function, member variables can be referenced just like local function variables or parameters. This can lead to some confusion:
class Foo
{
int a, b;
public:
void Set(int a, int b)
{
a = a; // Erh,
b = b; // wat???
}
};
There are many ways to work around this, but one of the most common is to prefix member variables with m_.
Your code runs afoul of this, apparently the original in C passed the array to heapify, and the array was in a local variable a. When you made a into a member, leaving the variable name exactly the same allowed you not to miss the fact that you no-longer need to pass it to the object (and indeed, your heapify member function no-longer takes an array as a pointer, leading to one of your compile errors).
The next problem we encounter, not directly part of your problem yet, is your function Heap(). Firstly, it is private - you used class and haven't said public yet. But secondly, you have missed the significance of this function.
In C++ every struct/class has an implied function of the same name as the definition. For class Heap that would be Heap(). This is the 'default constructor'. This is the function that will be executed any time someone creates an instance of Heap without any parameters.
That means it's going to be invoked when the compiler creates a short-term temporary Heap, or when you create a vector of Heap()s and allocate a new temporary.
These functions have one purpose: To prepare the storage the object occupies for usage. You should try and avoid as much other work as possible until later. Using std::cin to populate members in a constructor is one of the most awful things you can do.
We now have a basis to begin to write the outer-shell of the code in a fashion that will work.
The last change is the replacement of "HEAPSIZE" with a class enum. This is part of encapsulation. You could leave HEAPSIZE as a #define but you should expose it within your class so that external code doesn't have to rely on it but can instead say things like Heap::Size or heapInstance.size() etc.
#include <iostream>
#include <cstdint> // for size_t etc
#include <array> // C++11 encapsulation for arrays.
struct Heap // Because we want to start 'public' not 'private'.
{
enum { Size = 10 };
private:
std::array<int, Size> m_array; // meaningful names ftw.
public:
Heap() // default constructor, do as little as possible.
: m_array() // says 'call m_array()s default ctor'
{}
// Function to load values from an istream into this heap.
void read(std::istream& in)
{
for (size_t i = 0; i < Size; ++i)
{
in >> m_array[i];
}
return in;
}
void write(std::ostream& out)
{
for (size_t i = 0; i < Size; ++i)
{
if (i > 0)
out << ','; // separator
out << m_array[i];
}
}
int heapify(size_t index)
{
// implement your code here.
}
}; // <-- important.
int main(int argc, const char* argv[])
{
Heap myHeap; // << constructed but not populated.
myHeap.load(std::cin); // read from cin
for (size_t i = 1; i < myHeap.Size; ++i)
{
myHeap.heapify(i);
}
myHead.write(std::cout);
return 0;
}
Lastly, we run into a simple, fundamental problem with your code. C++ does not have implicit multiplication. 2i is the number 2 with a suffix. It is not the same as 2 * i.
int l = 2 * i;
There is also a peculiarity with your code that suggests you are mixing between 0-based and 1-based implementation. Pick one and stick with it.
--- EDIT ---
Technically, this:
myHeap.load(std::cin); // read from cin
for (size_t i = 1; i < myHeap.Size; ++i)
{
myHeap.heapify(i);
}
is poor encapsulation. I wrote it this way to draw on the original code layout, but I want to point out that one reason for separating construction and initialization is that it allows initialization to be assured that everything is ready to go.
So, it would be more correct to move the heapify calls into the load function. After all, what better time to heapify than as we add new values, keeping the list in order the entire time.
for (size_t i = 0; i < Size; ++i)
{
in >> m_array[i];
heapify(i);
}
Now you've simplified your classes api, and users don't have to be aware of the internal machinery.
Heap myHeap;
myHeap.load(std::cin);
myHeap.write(std::cout);
Suppose in one program, I'm given:
class Foo {
int x;
double y;
char z;
};
class Bar {
Foo f1;
int t;
Foo f2;
};
int main() {
Bar b;
bar.f1.z = 'h';
bar.f2.z = 'w';
... some crap setting value of b;
FILE *f = fopen("dump", "wb"); // c-style file
fwrite(&b, sizeof(Bar), 1, f);
}
Suppose in another program, I have:
int main() {
File *f = fopen("dump", "rb");
std::string Foo = "int x; double y; char z;";
std::string Bar = "Foo f1; int t; Foo f2;";
// now, given this is it possible to read out
// the value of bar.f1.z and bar.f2.z set earlier?
}
What I'm asking is:
given I have the types of a class, can I figure out how C++ lays it out?
You need to research "serialization". There is a library, Boost Serialization, that people have been recommending.
FWIW, I recommend against using fwrite or std::ostream::write on classes, structures and unions. The compiler is allowed to insert padding between members, so there may be garbage written out. Also, pointers don't serialize very well.
To answer your question, in order to determine which structure to load data from, you need some kind of sentinel to indicate the object type. This can be anything from an enum to the name of the object.
Also investigate the Factory design pattern.
I'm not quite sure what you're asking, so I'll take a leap...
If you really need to figure out where the fields are in a struct, use offsetof.
Note the "POD" restriction in the linked page. This is a C macro, included in C++ for compatibility reasons. We are supposed to use member pointers instead these days, though member pointers don't address all the same problems.
"offsetof" basically imagines an instance of your struct at address zero, and then looks at the address of the field you're interested in. This goes horribly wrong if your struct/class uses multiple or virtual inheritance, since finding the field then involves (typically) a check in the virtual table. Since the imaginary instance at address zero doesn't exist, it doesn't have a virtual table pointer, so you probably get some kind of access violation crash.
Some compilers can cope with this, as they have replaced the traditional offsetof macro with an intrinsic that knows the layout of the struct without trying to do the imaginary-instance trickery. Even so, it's best not to rely on this.
For POD structs, though, offsetof is a convenient way to find the offset to a particular field, and a safe one in that it determines the actual offset irrespective of the alignment applied by your platform.
For the sizeof a field, you obviously just use sizeof. That just leaves platform-specific issues - different layout on different platforms etc due to alignment, endianness and so on ;-)
EDIT
Possibly a silly question, but why not fread the data from the file straight into in instance of the struct, doing essentially what you did with the fwrite but in reverse?
You get the same portability issues as above, meaning your code may not be able to read its own files if recompiled using different options, a different compiler or for a different platform. But for a single-platform app this kind of thing works very well.
You can't assume anything about the order of the bytes that represent Bar. If the file goes across system or that program is compiled with different flags then you'll be reading and writing in different orders.
I've seen a way around this, but it may only work for very simple types.
and I quote from a raknet tutorial:
#pragma pack(push, 1)
struct structName
{
unsigned char typeId; // Your type here
// Your data here
};
#pragma pack(pop)
Noticed the #pragma pack(push,1) and #pragma pack(pop) ? These force your compiler (in this case VC++), to pack the structure as byte-aligned. Check your compiler documentation to learn more.
You want serialization.
For the example that you give, it looks like you really need some sort of C parser that would parse the strings with your type declarations. Then you'd be able to interpret the bytes that you read from the file in the correct way.
Structs in C are laid out member to member in order of declaration. The compiler may insert padding between members according to platform-specific alignment needs. The size of the variables is also platform-specific.
If you have control over the class you can use member pointers. You definitely can do this. The question is whether or not you should...
class Metadata
{
public:
virtual int getOffset() = 0;
};
template <typename THost, typename TField>
class TypedMetadata : Metadata
{
private:
TField (THost::*memberPointer_);
TypedMetadata(TField (THost::*memberPointer))
{
memberPointer_ = memberPointer;
}
public:
static Metadata* getInstance(TField (THost::*memberPointer))
{
return new TypedMetadata<THost, TField>(memberPointer);
}
virtual int getOffset()
{
THost* host = 0;
int result = (int)&(host->*memberPointer_);
return result;
}
};
template<typename THost, typename TField>
Metadata* getTypeMetadata(TField (THost::*memberPointer))
{
return TypedMetadata<THost, TField>::getInstance(memberPointer);
}
class Contained
{
char foo[47];
};
class Container
{
private:
int x;
int y;
Contained contained;
char c1;
char* z;
char c2;
public:
static Metadata** getMetadata()
{
Metadata** metadata = new Metadata*[6];
metadata[0] = getTypeMetadata(&Container::x);
metadata[1] = getTypeMetadata(&Container::y);
metadata[2] = getTypeMetadata(&Container::contained);
metadata[3] = getTypeMetadata(&Container::c1);
metadata[4] = getTypeMetadata(&Container::z);
metadata[5] = getTypeMetadata(&Container::c2);
return metadata;
}
};
int main(array<System::String ^> ^args)
{
Metadata** metadata = Container::getMetadata();
std::cout << metadata[0]->getOffset() << std::endl;
std::cout << metadata[1]->getOffset() << std::endl;
std::cout << metadata[2]->getOffset() << std::endl;
std::cout << metadata[3]->getOffset() << std::endl;
std::cout << metadata[4]->getOffset() << std::endl;
std::cout << metadata[5]->getOffset() << std::endl;
return 0;
}
I had the following piece of code (simplified for this question):
struct StyleInfo
{
int width;
int height;
};
typedef int (StyleInfo::*StyleInfoMember);
void AddStyleInfoMembers(std::vector<StyleInfoMember>& members)
{
members.push_back(&StyleInfo::width);
members.push_back(&StyleInfo::height);
}
Now, we had to restructure this a bit, and we did something like this:
struct Rectangle
{
int width;
int height;
};
struct StyleInfo
{
Rectangle size;
};
typedef int (StyleInfo::*StyleInfoMember);
void AddStyleInfoMembers(std::vector<StyleInfoMember>& members)
{
members.push_back(&StyleInfo::size::width);
members.push_back(&StyleInfo::size::height);
}
If this all looks like a stupid thing to do, or if you feel there's a good opportunity to apply BOOST here for some reason, I must warn you that I really simplified it all down to the problem at hand:
error C3083: 'size': the symbol to the left of a '::' must be a type
The point I'm trying to make is that I don't know what the correct syntax is to use here. It might be that "StyleInfo" is not the correct type of take the address from to begin with, but in my project I can fix that sort of thing (there's a whole framework there). I simply don't know how to point to this member-within-a-member.
Remember a pointer to a member is just used like a member.
Obj x;
int y = (x.*)ptrMem;
But like normal members you can not access members of subclasses using the member access mechanism. So what you need to do is access it like you would access a member of the object (in your case via the size member).
#include <vector>
#include <iostream>
struct Rectangle
{
int width;
int height;
};
struct StyleInfo
{
Rectangle size;
};
typedef Rectangle (StyleInfo::*StyleInfoMember);
typedef int (Rectangle::*RectangleMember);
typedef std::pair<StyleInfoMember,RectangleMember> Access;
void AddStyleInfoMembers(std::vector<Access>& members)
{
members.push_back(std::make_pair(&StyleInfo::size,&Rectangle::width));
members.push_back(std::make_pair(&StyleInfo::size,&Rectangle::height));
}
int main()
{
std::vector<Access> data;
AddStyleInfoMembers(data);
StyleInfo obj;
obj.size.width = 10;
std::cout << obj.*(data[0].first).*(data[0].second) << std::endl;
}
This is not something I would recommend doing!
An alternative (that I recommend even less) is to find the byte offset from the beginning of the class and then just add this to the objects address. Obviously this will involve a lot of casting backwards and forwards so this looks even worse then the above.
Is it definitely possible? I honestly don't know, never having played much with pointer-to-member.
Suppose you were using non-POD types (I know you aren't, but the syntax would have to support it). Then pointer-to-member might have to encapsulate more than just an offset from the base pointer. There might be indirection as well, depending how multiple inheritance is implemented. With multiple levels of member indirection, this could get arbitrarily complicated, which is a lot to ask for a type that has to have fixed size.
Perhaps you need a vector of pairs, of types defined by:
typedef Rectangle (StyleInfo::*StyleInfoMember);
typedef int (Rectangle::*RectangleMember);
Apply each in turn to get where you want to be. Of course this still doesn't let you build a vector of mappings from a StyleInfo to arbitrary members-of-members-of StyleInfo, since they wouldn't all go through Rectangle. For that you may need to open a can of functors...
size (as in &StyleInfo::size::width) is not the name of a type.
try size->width or size.width instead, depending on how your 'AddStyleInfoMembers` knows about size at all.