Can I initialize a `constexpr static` member outside the class? - c++

I'm working with a variable-width communications format. The structs to handle it look something like this:
struct Header
{
int msgType = -1, len;
Header() { len = sizeof(*this); }
};
struct A : public Header
{
int x; char y;
A() { msgType = 1; len = sizeof(*this); }
};
// Further structs B, C, ... declared along the same lines
I would like to have a constexpr static member Header::MAX_SIZE which gives the max size of any of these derived classes, e.g. so I can allocate a buffer which is guaranteed to hold any such packet. So I'd like to do something like
struct Header
{
int msgType = -1, len;
constexpr static std::size_t MAX_SIZE;
Header() { len = sizeof(*this); }
};
// ... declaration of subclasses ...
inline Header::MAX_SIZE = std::max({ sizeof(A), sizeof(B), sizeof(C) });
I need the definition to come outside of the class because it depends on sizeof(A), etc., which in turn depend on the definition of Header.
It seems like this sort of thing should be unobjectionable: I'm giving the definition of the member in the same source file, and it can be computed at compile time. But I haven't found any way to tell the compiler to actually do this.

constexpr goes on the initializing declaration of a variable, so just put it outside the class:
struct Header
{
int msgType = -1, len;
static const std::size_t MAX_SIZE;
Header() { len = sizeof(*this); }
};
// ... declaration of subclasses ...
inline constexpr std::size_t Header::MAX_SIZE = std::max({ sizeof(A), sizeof(B), sizeof(C) });
Note that the implicit const must be spelled out in the declaration. The definition should go in the same header to avoid any translation unit seeing the declaration but not the inline, which is not allowed.

Related

Initializing C struct with unions declared in C++17

This question is specifically about options in C++17. Assuming following declaration in C library (I cannot change them):
typedef enum {
TYPEA = 0,
TYPEB = 2,
TYPEC = 4
} SPECIFIC_TYPE_t;
typedef struct {
uint16_t Method : 1;
uint16_t Access : 2;
uint16_t VendorSpecific : 1;
uint16_t Direction : 1;
uint16_t Persistent : 1;
uint16_t Internal : 4;
uint16_t Reserved : 6;
} PROPERTY_t;
typedef RESULT_t (*CB_DataPointRead_t) (void *Service, uint8_t Pinpoint, bool VendorSpecific,
uint16_t GroupID, uint16_t ElementID, void *Data,
uint8_t *DataLengthInOut);
typedef RESULT_t (*CB_DataPointWrite_t) (void *service, uint8_t Pinpoint, bool VendorSpecific,
uint16_t GroupID, uint16_t ElementID, void *Data,
uint8_t DataLength);
typedef struct {
uint16_t GroupID;
uint16_t ElementID;
uint8_t Pinpoint;
SPECIFIC_TYPE_t Type;
uint8_t Size;
PROPERTY_t Property;
union {
struct {
CB_DataPointRead_t Read;
CB_DataPointWrite_t Write;
} Callback;
struct {
void *Data;
} DirectAccess;
} AccessType;
} DataPoint_t;
Status in pure C
To initialize the last element designator initializers work well in C:
uint16t dataPointValue = 0;
const DataPoint_t firstDatapointConfig = {
/*...*/,
.DirectAccess = { (void*)&dataPointValue; }
};
Designator initializers in C++
They appear in C++20 and aren't compatible with C in many aspects.
Problem
I'd like to initialize variable like firstDatapointConfig as const qualified in C++17. So far I don't see a way other than write a function in C (compiled as C code) and return the initialized structure to a variable before use. I tried various ways, including gnuc++17 which handles designator initializers, except it tells me:
error: 'const DataPoint_t' has no non-static data member named 'DirectAccess'
and MSVC don't digest this method of initialization at all without C++20 enabled.
Addressing the last element outside initializer, don't work either:
datapoint.DirectAccess = { &value };
results in the following error:
error: 'struct DataPoint_t' has no member named 'DirectAccess'
Comment
It was much easier to use these structures in Rust after processing them through bindgen :-)
Question
Is there a way to initialize the variable of DataPoint_t type in C++17 with DirectAccess element filled with the right value?
Is there a way to initialize the variable of DataPoint_t type in C++17 with DirectAccess element filled with the right value?
Yes, even though my way of doing it is a little cumbersome. There may be easier ways:
// Make a usable type of that anonymous entity:
using AccessType_t = decltype(DataPoint_t::AccessType);
// Use the new type and prepare what you need:
AccessType_t at;
at.DirectAccess.Data = nullptr;
// initialize your DataPoint_t
const DataPoint_t firstDatapointConfig{1,2,3,TYPEA,4, PROPERTY_t{}, at};
If you do this a lot you could make a helper function:
using AccessType_t = decltype(DataPoint_t::AccessType);
using Callback_t = decltype(AccessType_t::Callback);
using DirectAccess_t = decltype(AccessType_t::DirectAccess);
template<class U>
constexpr auto init_AccessType(U u) {
AccessType_t at;
if constexpr (std::is_same_v<U,Callback_t>) {
at.Callback = u;
} else if constexpr (std::is_same_v<U,DirectAccess_t>) {
at.DirectAccess = u;
} else {
// uninitialized
}
return at;
}
const DataPoint_t firstDatapointConfig{1,2,3,TYPEA,4, PROPERTY_t{},
init_AccessType(DirectAccess_t{nullptr})};
I'd like to initialize variable like firstDatapointConfig as const qualified in C++17
With all that C++ has to offer:
constexpr DataPoint_t create_DataPoint_t(uint16_t *v) {
DataPoint_t r{};
r.AccessType.DirectAccess.Data = v;
return r;
}
const DataPoint_t firstDatapointConfig = create_DataPoint_t(&dataPointValue);
with designated initializers:
const DataPoint_t firstDatapointConfig2 = {
.AccessType = {
.DirectAccess = {
.Data = &dataPointValue
}
}
};
Addressing the last element outside initializer, don't work either:
datapoint.DirectAccess = { &value };
Because there is no such element, there is datapoint.AccessType.DirectAccess.Data.
To initialize the last element designator initializers work well in C:
uint16t dataPointValue = 0;
const DataPoint_t firstDatapointConfig = {
/*...*/,
.DirectAccess = { (void*)&dataPointValue; }
};
The presented code is invalid - uint16t is meant to be uint16_t and ; is a typo. And still after fixing the typos, no, the presented code is invalid in "pure C" and "does not work well" godbolt link. There is no such thing as DirectAccess in DataPoint_t - there is such member in the unnamed union declared inside DataPoint_t. You can do in C:
const DataPoint_t firstDatapointConfig3_in_C = {
.AccessType = {
.DirectAccess = { (void*)&dataPointValue }
}
};
or
const DataPoint_t firstDatapointConfig4_in_C = {
.AccessType.DirectAccess = { (void*)&dataPointValue }
};
or
const DataPoint_t firstDatapointConfig4_in_C = {
.AccessType.DirectAccess.Data = (void*)&dataPointValue
};
The cast to void* is superfluous - all pointers are implicitly converted to void*. Note that the following:
const DataPoint_t firstDatapointConfig5_in_C = {
.AccessType = { (void*)&dataPointValue }
};
would be equal to:
const DataPoint_t firstDatapointConfig6_in_C = {
.AccessType.Callback.Read = (void*)&dataPointValue
};
Most probably you are coding under -fms-extensions with GNU gcc or with MSVC, in which case you should be aware that you are using an extension that imports unnamed structure members to parent structure. The code is invalid in "pure C", it's using an extension to C.

Can't a class have static constexpr member instances of itself?

This code is giving me incomplete type error.
What is the problem? Isn't allowed for a class to have static member instances of itself?
Is there a way to achieve the same result?
struct Size
{
const unsigned int width;
const unsigned int height;
static constexpr Size big = { 480, 240 };
static constexpr Size small = { 210, 170 };
private:
Size( ) = default;
};
A class is allowed to have a static member of the same type. However, a class is incomplete until the end of its definition, and an object cannot be defined with incomplete type. You can declare an object with incomplete type, and define it later where it is complete (outside the class).
struct Size
{
const unsigned int width;
const unsigned int height;
static const Size big;
static const Size small;
private:
Size( ) = default;
};
const Size Size::big = { 480, 240 };
const Size Size::small = { 210, 170 };
see this here: http://coliru.stacked-crooked.com/a/f43395e5d08a3952
This doesn't work for constexpr members, however.
Is there a way to achieve the same result?
By "the same result", do you specifically intend the constexpr-ness of
Size::big and Size::small? In that case maybe this would be close enough:
struct Size
{
const unsigned int width = 0;
const unsigned int height = 0;
static constexpr Size big() {
return Size { 480, 240 };
}
static constexpr Size small() {
return Size { 210, 170 };
}
private:
constexpr Size() = default;
constexpr Size(int w, int h )
: width(w),height(h){}
};
static_assert(Size::big().width == 480,"");
static_assert(Size::small().height == 170,"");
As a workaround you can use a separate base class which definition is complete when defining the constants in the derived class.
struct size_impl
{
//data members and functions here
unsigned int width;
unsigned int height;
};
struct size: public size_impl
{
//create the constants as instantiations of size_impl
static constexpr size_impl big{480,240};
static constexpr size_impl small{210,170};
//provide implicit conversion constructor and assignment operator
constexpr size(const size_impl& s):size_impl(s){}
using size_impl::operator=;
//put all other constructors here
};
//test:
constexpr size a = size::big;
You can put the base class in a separate namespace to hide its definition if you want to.
The code compiles with clang and gcc

Initialize static const multidimensional array with inferred dimensions inside class definition

Since C++11, one can initialize static const built-in types inside a class definition, like so:
class A {
public:
static const unsigned int val = 0; //allowed
};
However, doing this in Visual C++ 2013 with an array gives me an error telling me that this is not allowed:
class B {
public:
static const unsigned int val[][2] = { { 0, 1 } }; //not allowed
};
The error message simply reads "a member of type const unsigned int [][2] cannot have an in-class initializer." Instead, I'm forced to do the following:
class C {
public:
static const unsigned int val[][2];
};
const unsigned int C::val[][2] = { { 0, 1 } };
This is unfortunate because I have code which relies on the size of val, and I want to be able to change the contents of val without having to remember to go back and change a constant somewhere. Is there a different way of doing this that allows me to use sizeof on val from any point in the file below the declaration?
Your array must be a constexpr (clang and gcc specify it in their error messages) :
class B {
public:
static constexpr const unsigned int val[][2] = { { 0, 1 } };
// ^^^^^^^^
};
See it working here.
Visual Studio CTP 2013 (a "beta" version, that should be avoided for production) provides support for constepxr, which should be available in future releases, too.
EDIT :
If your compiler does not support constexpr (hopefully for you, not for too long), then you cant do in-class initialization of your static array, and must do the old way :
class C {
public:
static const unsigned int val[][2];
};
const unsigned int C::val[][2] = { { 0, 1 } };
If your array type is complete (if you declare all the array dimensions), then sizeof can be applied (the compiler knows how many elements to expect) :
;
class C {
public:
static const unsigned int val[2][2]; // Specify all dimensions.
void foo() { cout << sizeof(C::val); } // OK
};
const unsigned int C::val[][2] = { { 0, 1 } , { 2, 3 } };
int main() {
C c;
c.foo();
return 0;
}

C++: initializing template constructor/routine declared in header file?

I have a template defined in my header file as follows:
template<typename T> class BoundedBuffer {
unsigned int size;
T entries[];
public:
BoundedBuffer( const unsigned int size = 10 );
void insert( T elem );
T remove();
};
However, when I try to initialize the constructor:
BoundedBuffer<T>::BoundedBuffer( const unsigned int size = 10 ) size(size) {
// create array of entries
entries = new T[size];
// initialize all entries to null
for(int i = 0; i < size; i++)
entries[i] = null;
}
I get the following error (the first line of the previous code block is 17):
q1buffer.cc:17: error: âTâ was not declared in this scope
q1buffer.cc:17: error: template argument 1 is invalid
q1buffer.cc:17: error: expected initializer before âsizeâ
The right syntax is:
template <typename T>
BoundedBuffer<T>::BoundedBuffer(const unsigned int size) : size(size) {
// create array of entries
entries = new T[size];
// initialize all entries to null
for(int i = 0; i < size; i++)
entries[i] = null;
}
Note that optional parameters should not be declared in functions definitions but ony in functions declarations.
class aaa
{
// declaration
void xxx(int w = 10);
};
// definition
void aaa::xxx(int w)
{
...
}
Note that everything for templated class should stay in H files.
"They must be in the same translation unit. It is quite common in some libraries to separate the template implementation into a .tpp (or some other extension) file that is then included in the .h where the template is declared." as Michael Price said.
Templates are not normal types and they cannot be linked.
They are instantiated only when requested.
Note that constructors fields initializers need the ":" character.
class MyClass
{
public:
int x;
MyClass() : x(10) { /* note that i used : character */ }
};
You have to implement all methods of the template in the header, because users of the template need to be able see those methods to instantiate it for a given type.
Your declaration should be:
template< typename T >
BoundedBuffer<T>::BoundedBuffer( const unsigned int size ) : size( size ) {...}
Note that it also has to be in the header file, as mentioned by #Dean Povey.

in-class initialization of non-integral static data

So I just learned via a compiler error that in-class initialization of arrays is invalid (why?). Now I would like to have some arrays initialized in a template class, and unfortunatly the contents depend on the template parameter. A condensed testcase looks like this:
template<typename T>
struct A {
T x;
static const int len = sizeof(T); // this is of course fine
static const int table[4] = { 0, len, 2*len, 3*len }; //this not
}
Any idea how to pull out the constant array?
EDIT: Added the 'int's.
Just as you'd do it without templates; put the initialization outside the class' declaration:
template<class T>
const int A<T>::table[4] = { 0, len, 2*len, 3*len };
class Y
{
const int c3 = 7; // error: not static
static int c4 = 7; // error: not const static const
float c5 = 7; // error not integral
};
So why do these inconvenient restrictions exist? A class is typically declared in a header file and a header file is typically included into many translation units. However, to avoid complicated linker rules, C++ requires that every object has a unique definition. That rule would be broken if C++ allowed in-class definition of entities that needed to be stored in memory as objects.
for more detail read : How do I define an in-class constant?
template <typename T, int index>
struct Table {
static const len = sizeof(T);
static const value = len*index;
};