What does vk::DeviceQueueCreateFlags() actually do? - c++

The C like way to initialize this structure is:
VkDeviceQueueCreateInfo queueCreateInfo = {};
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queueCreateInfo.queueFamilyIndex = queueFamily;
queueCreateInfo.queueCount = 1;
queueCreateInfo.pQueuePriorities = &queuePriority;
The C++ way, using and abusing the vulkan.hpp header, is:
vk::DeviceQueueCreateInfo deviceQueueCreateInfo(vk::DeviceQueueCreateFlags(), static_cast<uint32_t>(graphicsQueueFamilyIndex), 1, &queuePriority);
It seems that a lot of work is encapsulated in the function vk::DeviceQueueCreateFlags().
However looking through the source with my editor is not revealing anything useful. I was hoping someone with more experience could provide some information as to what the function is doing.

It's an alias for vk::Flags, which is a template for dealing with Vulkan bitfields in a type-safe manor.
You want enumerations to be type-safe; you don't want implicit conversions to/from integers. So in C++, you do this by defining the enumeration to be an enum class.
That's fine for regular enumerations. But bitfields are special; they're combinations of enumerators from a specific enumeration. The typical post-C++11 solution is to just give the enumeration type overloaded operators, so that you can apply & and | to the enumeration type itself.
Personally, this solution always rubbed me the wrong way. To me, a strong enumeration type should hold one of the enum values, not several of them. So using operator overloading to effectively allow an enumeration value to lie didn't sit well with me.
Apparently I'm not alone in that, because the writers of vulkan.hpp opted for a different solution. They created a template class vk::Flags, which takes two template parameters. One of them is the enumeration that contains all of the valid bitflags. The second parameter is the "enumeration" that represents the concatenation of multiple flags. Which is something that the vulkan.xml specification file actually understands: the difference between the field containing the possible bits and the field which is an aggregation of bits.
vk::Flags can be given bits from the bitfield, and you can manipulate it with bits from that bitfield via some of the bitwise operators. And it is implicitly convertible to the type that represents the aggregation of bits.
So DeviceQueueCreateInfo is just an alias for Flags<DeviceQueueCreateFlagBits, VkDeviceQueueCreateFlags>, which is a bitfield that takes device creation bits and aggregates them into the flags aggregate.

Related

Is there automatic name lookup for enumerators in c++?

As for as I know, currently in c++ there is no support for this.
For example,
class C{
struct{
enum {defaulted, opt1, opt2, ...} flag1;
enum {defaulted, optA, optB, ...} flag2;
} flags;
...
};
Now suppose we have C obj, to use the flag one would do
obj.flags.flag1 = obj.flags.opt1;
which is unnecessarily verbose.
However as the type of obj.flags.flag1 is known, in theory the compiler could lookup the name opt1 in that scope and save some typing.
Because the flags are not used anywhere else, it is preferred not to give names to the types (in fact it is difficult to come up with appropriate names). using enum syntax in c++20 does not solve this, because: it 1) requires the enum types to be named; 2) using multiple enums may lead to name collision, as shown here, since both enums have a defaulted member.
What will be the difficulties to implement this? Has this been proposed to the C++ standard committee and/or implemented in some compiler?
There could easily be an object named opt1 that could be converted to the appropriate enumeration type. It's better for name lookup to behave consistently rather than make special cases based on the types of other operands (partly because types are usually determined after name lookup).

Why do C++ container and iterator requirements specify size_type and difference_type as integer types?

The [container.requirements] specify that for a Container type X, X::difference_type and X::size_type are signed and unsigned integer types (defined by [basic.fundamental]/2). The same goes for iterators' difference type.
What are the reasons that these are not allowed to be an enumeration type or any object type?
Edit: Of course the types above should satisfy some necessary requirements, e.g., support some arithmetic operations. I don't expect X::size_type defined to be struct Empty {}; with no operators to work.
My motivation is that I want to define X::size_type and X::difference_type as user-defined types whose defined operators form an affine space and therefore differ from operators defined for builtin integer types. (Affine spaces would mean that I only define operations that make mathematical sense for the types and mitigate common bugs, see an example here.)
Note: this question is not about signed versus unsigned. I know about this: Is using an unsigned rather than signed int more likely to cause bugs? Why?
One possible answer I can think of is that users might want to use types as non-type template parameters, but that could still work with enumeration types.
Edit: Another possible answer is perhaps that the "necessary requirements" in my edit above are simply not defined yet and it was easier to go with what already exists in the standard.

enum [tag] [: type] {enum-list} [declarator] in C++ - is this legal?

I am working with a custom enumerated type in C++, but it does not have many values. I want to try to reduce the size that they take up, and I've heard that enum types are always integers by default. I then came across the MSDN entry on C++ enumerations, and found the following syntax very interesting:
enum [: type] {enum-list};
Sure enough, it compiled with what I wanted (VS2008) when I did the following:
enum plane : unsigned char { xy, xz, yz };
Now, you can see from my enumeration constants that I don't need much in terms of space - an unsigned char type would be perfect for my uses.
However, I have to say, I've never seen this form used anywhere else on the internet - most don't even seem aware of it. I'm trying to make this code cross-platform (and possibly for use on embedded systems), so it left me wondering... Is this proper C++ syntax, or only supported by the MSVC compiler?
Edit: It seems that this feature is now part of C++11 and above, and is called scoped enumerations.
This is non-standard, but it is expected to be a part of the C++0x standard. For me, when I compile in Visual Studio 2005 with warning level set to maximum I get the following warning:
warning C4480: nonstandard extension used: specifying underlying type for enum 'Test'
From the Wikipedia page for C++0x:
In standard C++, enumerations are not type-safe. They are effectively
integers, even when the enumeration types are distinct. This allows
the comparison between two enum values of different enumeration types.
The only safety that C++03 provides is that an integer or a value of
one enum type does not convert implicitly to another enum type.
Additionally, the underlying integral type is implementation-defined;
code that depends on the size of the enumeration is therefore
non-portable. Lastly, enumeration values are scoped to the enclosing
scope. Thus, it is not possible for two separate enumerations to have
matching member names.
Additionally, C++0x will allow standard enumerations to provide
explicit scoping as well as the definition of the underlying type:
enum Enum3 : unsigned long {Val1 = 1, Val2};
As 0A0D's said, the notation you're using is non-Standard in C++03, but has been adopted by C++11 under the term "scoped enums".
If that's not soon enough for you, then you can consider explicitly specifying the bit field width used for the enum fields in the size-critical structures in which they're embedded. This approach is ugly - I mention if for completeness; if it was a good solution the notation above wouldn't be being adopted for C++11. One problem is that you rely on an optional compiler warning to detect too-small bit-fields to hold the possible values, and may have to manually review them as the enumeration values change.
For example:
enum E
{
A, B, C
};
struct X
{
E e1 : 2;
E e2 : 2;
E e3 : 2;
E e4 : 2;
};
Note: the enum may occupy more bits than requested - on GCC 4.5.2 with no explicit compiler options, sizeof(X) above is still 4....
It's perfectly legal. Embedding the type in an object to save space however will not be effective without specifically ensuring the alignment. If you leave default alignment, padding bytes will be added (depending on the architecture) usually to a quad-word boundary.

How is long long implemented in C++?

In C++, as far as I know, all data types are implemented as classes. ( Don't know if it is right, but I read it as a justification for statements such as int a(5); which calls the parametric constructor of int. )
If so, how are long and short implemented? I just found out that long long and short short are valid types but short long and long short are not (Checked the latter ones just because it sounds funny!)
Similarly, how are signed and unsigned implemented?
PS. By implemented, what I mean is "Is written using C/C++ features or is it written at a lower level in the compiler itself".
So the equivalent parts of declaration of variable of a basic type and a userdefined object or variable is (read downwards)
auto|register|static|extern <=> auto|register|static|extern
const <=> const
(signed|unsigned)(long|short)datatype <=> class name etc
variable name <=> object/variable name
? Is that assusmption correct?
On the particular question, long long is implemented by the compiler in a compiler + platform specific way (usually more platform than compiler dependent).
As to the original misconception, no, not all types are classes in C++. The languages tries to provide a uniform syntax in as much as possible for all types, trying to have all types behave similarly in as much as possible, and be used in a similar way. As a matter of fact, it is actually quite the other way around: C++ tries in as much as possible to have classes behave like primitive types (value semantics).
The particular reason to be able to initialize an integer that way is actually quite related to classes, just in a different way. In a class constructor definition there are initializer lists that define how each one of the members is initialized before the constructor block is executed. The syntax for each initializer element in the list is basically (I would have to lookup the exact definition): member_name( initializer ), so for example you would get:
class my_int_vector {
int * p;
int size;
public:
my_int_vector() : p(0), size(0) {}
//...
}
Both pointers and integers are fundamental types, but they can be initialized in a way similar to that of classes. If that type of initialization was not allowed for fundamental types, and only the type name = value; syntax was allowed, the initializer list syntax would have to be extended, and you would not be able to seamlessly change those types at a later time (say, change the int to a atomic_int).
Built-in data types are not implemented as classes. They just have some syntactic similarities.
short, long, int and every other built-in type name are keywords which get treated specially by the compiler. So, in a nutshell, they're implemented as magic. They're not classes, they're just themselves.
So no, these types cannot be implemented in terms of other language features, the way std::string or other standard library components can. The built-in types are a fundamental part of the core language.
Not all data types are classes in C++. The primitive C data types are not (they're called scalars). They're not "implemented" at all, but rather they form a core feature of the language that has a direct translation to machine code. The syntax int i(5); is equivalent to C's int i = 5; and initializes the variable at declaration time.
You're laboring under a number of misconceptions:
Not all data types are implemented as classes in C++. In fact, only class types are implemented as classes. Enums, pointers and arrays, in addition to the fundamental types, are not implemented as classes.
short short is not a valid type. The signed integral types in
C++03 are signed char, short, int, and long. Period. C++11
adds long long, and allows the implementation to add others.
I'm not sure what you mean by "implemented" here. On almost all modern machine, all of the integral types (signed or unsigned) are directly supported in hardware; the C++ compiler just generates the appropriate hardware instructions. (On older machines, long, and sometimes even int or short, often required function calls for the basic operations, and on some rare and exotic machines, unsigned arithmetic requires added instructions.)
How you write a definition is a question of syntax, not of implementation. Both T v(i); and T v = i; are legal if the type supports copy; for all but class types, they are perfectly identical. (With a modern compiler; I've used some compilers in the past that had bugs in this regard. But that's a fairly distant past.)

what is a type in C++?

What all constructs(class,struct,union) classify as types in C++? Can anyone explain the rationale behind calling and qualifying certain C++ constructs as a 'type'.
$3.9/1 - "There are two kinds of
types: fundamental types and compound
types. Types describe objects (1.8),
references (8.3.2), or functions
(8.3.5). ]"
Fundamental types are char, int, bool and so on.
Compound types are arrays, enums, classes, references, unions etc
A variable contains a value.
A type is a specification of the value. (eg, number, text, date, person, truck)
All variables must have a type, because they must hold strictly defined values.
Types can be built-in primitives (such as int), custom types (such as enums and classes), or some other things.
Other answers address the kinds of types C++ makes available, so I'll address the motivation part. Note that C++ didn't invent the notion of a data type. Quoting from the Wikipedia entry on Type system.
a type system may be defined as "a
tractable syntactic framework for
classifying phrases according to the
kinds of values they compute"
Another interesting definition from the Data Type page:
a data type (or datatype) is a
classification identifying one of
various types of data, such as
floating-point, integer, or Boolean,
stating the possible values for that
type, the operations that can be done
on that type, and the way the values
of that type are stored
Note that this last one is very close to what C++ means by "type". Perhaps obvious for the built-in (fundamental) types like bool:
possible values are true and false
operations - per definition of arithmetic operators that can accept bool as argument
the way it's stored - actually not mandated by the C++ standard, but one can guess that on some systems a type requiring only a single bit can be stored efficiently (although I think most C++ systems don't do this optimization).
For more complex, user created, types, the situation is more difficult. Consider enum types: you know exactly the range of values a variable of an enum type can get. What about struct and class? There, also, your type declaration tells the compiler what possible values the struct can have, what operations you can do on it (operator overloading and functions accepting objects of this type), and it will even infer how to store it.
Re range of values, although huge, remember it's finite. Even a struct with N 32-bit integers has a finite range of possible values, which is 2^(32N).
Cite from the book "Bjarne Stroustrup - Programming Principles and Practice Using C++", page 77, chapter 3.8:
A type defines a set of possible values and a set of operations (for an object).
An object is some memory that holds a value of a given type.
A value is a set of bits in memory interpreted according to a type.
A variable is a named object.
A declaration is a statement that gives a name to an object.
A definition is a declaration that sets aside memory for an object.
Sounds like a matter of semantics to me...A type refers to something with a construct that can be used to describe it in a away that conforms to traditional Object Oriented concepts(properties and methods). Anything that isn't called a type is probably created with a less robust construct.