I know that c and c++ standards state that if you don't specify first element's value a start value of enum will default to 0.
But e.g. in Linux kernel sources I faced strange declarations dozens of times. e.g. numa_faults_stats:
enum numa_faults_stats {
NUMA_MEM = 0,
NUMA_CPU,
NUMA_MEMBUF,
NUMA_CPUBUF
};
What is the need for explicitly set first element of this enum to 0?
Related post.
There are very many rules for various things in C and C++: this being one of them. Sometimes it's nice to be explicit, for clarity.
Another common one is to use variable names in function prototypes (only the types are needed). Yet another is a return 0; in main in either language. The explicit use of public and private in a C++ class or struct is another.
You can use enums without care its value like only using it comprasions with each other. But sometimes its value is important. You may use is as an index of an array. eg.
struct NUMA Numa[N];
Numa[NUMA_MEM];
Numa[NUMA_CPU];
In this case it is definitly good idea explicitly assing value even it is default equal. You emphasize that its value has usage in code.
Related
In one of the classes that I am working I found some thing like this in the header file:
// Flags
union
{
DWORD _flags;
struct {
unsigned _fVar1:1;
unsigned _fVar2:1;
unsigned _fVar3:1;
unsigned _fVar4:1;
};
};
In some of the class's member functions, I have seen _flags being set directly like _flags = 3;.
I have also seen the members in the struct being set directly, like _fVar1 = 0 and being compared against.
I am trying to remove _fVar1, I am not sure what it will do to other places where _flags and other _fVar# are accessed or set.
For instance, does setting _flags = 3 means that _fVar1 and _fVar2 will be 1 and _fVar3 and _fVar4 will be 0? Would removing or adding to the struct means I have to make corresponding changes to codes that touches any of the other members in the union?
Anonymous member structs (classes) are not allowed in C++, so the program is ill-formed as far as the standard is concerned.
Accessing non-active member of a union has undefined behaviour.
So in short: Whatever it does is up to the compiler.
Both of those are allowed in C (the former wasn't allowed until C11, the latter until C99), and by some compilers, as an extension in C++ (and as an extension in earlier versions of C). Let us assume that you use such compiler.
For instance, does setting _flags = 3 means that _fVar1 and _fVar2 will be 1 and _fVar3 and _fVar4 will be 0?
That is probably the intention. However, the behaviour depends on the representation that the compiler has chosen for the bit fields.
Without making assumptions about the representation, the only sensible thing that you can use the union for is to set all flags to 0 (_flags = 0), or all flags to 1 (_flags = -1).
Would removing or adding to the struct means I have to make corresponding changes to codes that touches any of the other members in the union?
Yes, unless the code touches all of the members equally, like the two examples above.
There is nothing very special about it. This is simply a union of an integer variable and a struct with bit fields.
Every bitfield in the struct is one bit length, so it can be used to access individual bits in the integer.
In particular, i got following code in library interface:
typedef enum
{
state1,
state2,
state3,
state4,
state5,
state_error = -1,
} State;
I strictly forbidden to break ABI. However, I want to add state6 and state7. Will it break ABI?
I found here some tip, but i somewhat doubt if it`s my case?
You can...
append new enumerators to an existing enum.
Exeption: if that leads to the compiler choosing a larger underlying type for the enum,that makes the change binary-incompatible. Unfortunately, compilers have some leeway to choose the underlying type, so from an API-design perspective it's recommended to add a Max.... enumerator with an explicit large value (=255, =1<<15, etc) to create an interval of numeric enumerator values that is guaranteed to fit into the chosen underlying type, whatever that may be.
Your question is a nice example why long-term maintaining of ABI compatibility is a difficult task. The core of the problem here is that the compatibility depends not just on the given type, but also on how it is used in function/method prototypes or complex types (e.g. structures, unions etc.).
(1) If the enumeration is used strictly as an input into the library (e.g. as a parameter of a function which just changes of behavior the function/library), then it keeps the compatibility: You changed the contract in a way which can never hurt the customer i.e. the calling application. Old applications shall never use the new value and will get the old behavior, new applications just get more options.
(2) If the enumeration is used anywhere as an output from the library (e.g. return value or function filling some address provided by caller aka an output parameter), the change would break the ABI. Consider the enumeration to be a contract saying "the application never sees values other then those listed". Adding new enum member would break this contract because old applications could now see values they never counted with.
That is at least, if there are no measures to protect old applications from falling into these troubles. Generally speaking, the library still can output the new value, but never for any valid input potentially provided by the old applications.
There are some design patterns allowing such enum expansions:
E.g. the library can provide an initialization function which allows to specify version of ABI the application is ready for. Old application ask for version 1.0 and never get the new value on input; newer application specify 1.1. or 2.0 or if the new enum value as added in the version 1.1, and then it may get the new value.)
Or, if a function DoSomething() is getting some flags on input, you may add a new flag where application can specify it's ready to see the new output value.
Or, if that's not possible, new version of the library may add a new function DoSomethingEx() which provides the more complex behavior than the original DoSomething(). DoSomethingEx() now can return the new enum value, the DoSomething() cannot.
As a side note if you ever need to add such DoSomethingEx(), do it in a way that allows similar expansions in the future. For consistency, it's usually a good idea to design it so that DoSomethingEx() with default flags (usually zero) behaves the same way DoSomething() and only with some new flag(s) it offers a different and more complex behavior.
Drawback of course is that the library implementation has to check what the application is ready for and provide a behavior compatible for expectations of old applications. It does not seem as much but over time and many versions of the library, there may be dozens of such checks accumulated in the library implementation, making it more complex and harder to maintain.
The quote actually is your case. Simple add the new enum values at the end (but before the state_error as it has a different value) and it should be binary compatible, unless, as mentioned in the quote you provided, the compiler chooses to use a different sized type, which doesn't seem likely in the case of such a small enum.
The best way is to try and check: a simple sizeof(State) executed before and after the changes should be enough (though you also might want to check if the values are still the same).
Take a look at the highest-valued enumerator-id: state3 is 2.
That means, even if the compiler should have chosen char as the underlying type, you can comfortably fit 100+ additional enumerator-ids there, without risking damage to binary compatibility.
That pre-supposes that the users supply a value of the iterator, instead of ever reading one, though.
Writing code like
struct S
{
this() // compile-time error
{
}
}
gives me an error message saying
default constructor for structs only allowed with #disable and no body.
Why??
This is one of cases much more tricky than one can initially expect.
One of important and useful features D has over C++ is that every single type (including all user types) has some initial non-garbage value that can be evaluated at compile-time. It is used as T.init and has two important use cases:
Template constraints can use T.init value to check if certain operations can be done on given type (quoting Kenji Hara's snippet):
template isSomething(T) {
enum isSomething = is(typeof({
//T t1; // not good if T is nested struct, or has #disable this()
//T t2 = void; auto x = t2; // not good if T is non-mutable type
T t = T.init; // avoid default construct check
...use t...
}));
}
Your variables are always initialized properly unless you explicitly use int i = void syntax. No garbage possible.
Given that, difficult question arises. Should we guarantee that T() and T.init are the same (as many programmers coming from C++ will expect) or allow default construction that may easily destroy that guarantee. As far as I know, decision was made that first approach is safer, despite being surprising.
However, discussions keep popping with various improvements proposed (for example, allowing CTFE-able default constructor). One such thread has appeared very recently.
It stems from the fact that all types in D must have a default value. There are quite a few places where a type's init value gets used, including stuff like default-initializing member variables and default-initializing every value in an array when it's allocated, and init needs to be known at compile for a number of those cases. Having init provides quite a few benefits, but it does get in the way of having a default constructor.
A true default constructor would need to be used in all of the places that init is used (or it wouldn't be the default), but allowing arbitrary code to run in a number of the cases that init is used would be problematic at best. At minimum, you'd probably be forced to make it CTFE-able and possibly pure. And as soon as you start putting restrictions like that on it, pretty soon, you might as well just directly initialize all of the member variables to what you want (which is what happens with init), as you wouldn't be gaining much (if anything) over that, which would make having default constructors pretty useless.
It might be possible to have both init and a default constructor, but then the question comes up as to when one is used over the other, and the default constructor wouldn't really be the default anymore. Not to mention, it could become very confusing to developers as to when the init value was used and when the default constructor was used.
Now, we do have the ability to #disable the init value of a struct (which causes its own set of problems), in which case, it would be illegal to use that struct in any situation that required init. So, it might be possible to then have a default constructor which could run arbitrary code at runtime, but what the exact consequences of that would be, I don't know. However, I'm sure that there are cases where people would want to have a default constructor that would require init and therefore wouldn't work, because it had been #disabled (things like declaring arrays of the type would probably be one of them).
So, as you can see, by doing what D has done with init, it's made the whole question of default constructors much more complicated and problematic than it is in other languages.
The normal way to get something akin to default construction is to use a static opCall. Something like
struct S
{
static S opCall()
{
//Create S with the values that you want and return it.
}
}
Then whenever you use S() - e.g.
auto s = S();
then the static opCall gets called, and you get a value that was created at runtime. However, S.init will still be used any place that it was before (including S s;), and the static opCall will only be used when S() is used explicitly. But if you couple that with #disable this() (which disables the init property), then you get something akin to what I described earlier where we might have default constructors with an #disabled init.
We may or may not end up with default constructors being added to the language eventually, but there are a number of technical problems with adding them due to how init and the language work, and Walter Bright doesn't think that they should be added. So, for default constructors to be added to the language, someone would have to come up with a really compelling design which appropriately resolves all of the issues (including convincing Walter), and I don't expect that to happen, but we'll see.
(i'm using Visual C+++ 2010)
suppose i have defined a tuple like this:
typedef std::tr1::tuple<
int //i want to set its default value to 9
, double //i want to set its default value to 3.3
, int //i want to set its default value to 2
, double //i want to set its default value to -7.2
> Mytuple;
i can do that in a struct. but i wonder if it is possible to do that in std::tr1::tuple.
Besides, i want to know when shoud i use std::tr1:tuple or struct?
anyone can help me?
A tuple is not a magical replacement for a struct. Their purposes are very different. A struct is, first and foremost, a language construct. A tuple is a library construct.
Structs get to have default values because the language says that you can write constructors to give them default values. The language then takes responsibility to call that constructor to initialize the struct. tuple, as a library construct, has no such capabilities, anymore than you can give std::vector<T> a default T that it will always automatically use. You can provide initial values for each member, but you can't give it defaults.
And if you think about it, you wouldn't want to. Imagine if someone could say that every tuple<int, float> was always created with 3 and 54.221. Even if some other code created it that knew nothing about this rule, it would have to be followed, (just as constructors for the type are used everywhere that type is used). Remember: every tuple<int, float> is the same type.
Really, a tuple is a substitute for the inability to perform reflection on a struct and do compile-time iteration over its members. The other main reason they exist is to be able to have compile-time dynamic structures (that is, the ability to create aggregates of types based on compile-time arguments, rather than a static list directly written into a file).
So unless you need to use std::tie (for effectively returning multiple values), iteration over members (ie: call some template function for each member of an object), or some similar specialized code, you should be using a struct.
Basically, if I don't do all objects get a copy of all the enum values?
ps: as always, references for your answer are always welcome...
With the enum keyword you are actually defining a type, not defining storage like a data member.
It's like defining an inner class, like this:
class X
{
private:
class InnerClass
{
...
};
};
The InnerClass definition is just a definition. It just defines the type within the context of the outer class X, so InnerClass can only be referred to as X::InnerClass.
But it definitely doesn't take any space in the instances of class X.
Regarding the remark on enums: The enum values are actually integers, and these integers are used in the code where needed. Normally there is no central storage of all the enum values. The enum just defines a mapping between a token and a numerical value and everywhere the token is used in your code, the compiler replaces it with the numerical value.
You cannot declare a nested type (such as enum) static. Syntactically.
You can't, it doesn't make sense. Each instance doesn't store the values, they would store a value that happens to come from the enumerated values.
Enums don't hold all possible values. The various possible values only exist in the code during compile time. At run time, each instance of an enum is essentially an integral value.
enum {..}s don't really take up any space in your program. Only objects declared as that enumerated type take up any space. Even then, it is unlikely to be more space than fits in a machine register (since they are comptaible with integer types).
The only place there will ever be any storage associated with all the enumerated names and values defined for the type is during compilation. At runtime, all that information is unnessecary (and thus is not there).