I am splitting my code into declarations and definitions. I didn't get any problems until trying to do anything with this constant struct, consisting of vectors. Leaving this code in header leads to multiple definitions type of errors.
// Core.h:
const struct ConstData {
vector<int> numbers1 = { 1, 2, 3, 4, 5 };
vector<int> numbers2 = { 0, 10, 20, 30 };
} Constants;
I tried moving this code into cpp file and using extern with the struct in the header, but that didn't help. On use cases of struct fields in other files I was getting undeclared identifiers type of errors.
// Core.cpp:
const struct ConstData {
vector<int> numbers1 = { 1, 2, 3, 4, 5 };
vector<int> numbers2 = { 0, 10, 20, 30 };
} Constants;
// Core.h:
extern const struct ConstData Constants;
Tried putting the struct, with uninitialised fields, before the extern. Thought that might help, so compiler sees with what type of struct it's working and what fields it has. But that is considered redefinition, since I have same struct in cpp file.
// Core.h:
const struct ConstData {
vector<int> numbers1;
vector<int> numbers2;
};
extern const struct ConstData Constants;
// Core.cpp:
const struct ConstData {
vector<int> numbers1 = { 1, 2, 3, 4, 5 };
vector<int> numbers2 = { 0, 10, 20, 30 };
} Constants;
I'm kinda stuck at this point. Looking up how people are dealing with this issue didn't lead me to much success. In extern docs of Microsoft was said that const modifier changes linkage type (internal or external). So I tried all of the above again, but playing with the const at the same time — no progress on that. Maybe I missed something..
Hope I have supplied enough info and that community will be able to help me!
This code
const struct ConstData {
vector<int> numbers1 = { 1, 2, 3, 4, 5 };
vector<int> numbers2 = { 0, 10, 20, 30 };
} Constants;
...declares both the structure type ConstData and the variable Constants. You cannot re-declare the structure in the .cpp file if you already declare it in the header file.
You want to split both declarations in the header and only initialize the variable in the .cpp file:
// Core.h
#include <vector>
struct ConstData {
std::vector<int> numbers1;
std::vector<int> numbers2;
};
extern const ConstData Constants;
// Core.cpp
#include <Core.h>
const ConstData Constants{
{ 1, 2, 3, 4, 5 },
{ 0, 10, 20, 30 }
};
I would recommend a struct with static members and std::array for the elements. Since c++17 you can declare this in header file:
#include <array>
struct ConstData {
static constexpr std::array numbers1 = { 1, 2, 3, 4, 5 };
static constexpr std::array numbers2 = { 0, 10, 20, 30 };
};
And use ConstData::number1 / ConstData::number2.
Before c++17 it is a bit more effort:
// Header file:
struct ConstData {
static constexpr std::array<int, 5> numbers1 = { 1, 2, 3, 4, 5 };
static constexpr std::array<int, 5> numbers2 = { 0, 10, 20, 30 };
};
// c++-File
constexpr std::array<int,5> ConstData::numbers1;
constexpr std::array<int,5> ConstData::numbers2;
These are compile-time structures. So they appear 1:1 in memory. Using std::vector always allocates memory and copies the elements at runtime.
Related
I am trying to initialize a structure through an inline constructor in C++ which has one member element is an array.
In the following example is it possible to initialize _dmac array in MplsTunnel_() constructor call?
typedef struct MplsTunnel_
{
uint32_t _port;
uint32_t _iflIndex;
uint16_t _vsi;
int _vcEncapAccess;
uint8_t _dmac[6];
MplsTunnel_() : _port(0), _iflIndex(0), _vsi(0), _vcEncapAccess(0) {}
} MplsTunnel_t;
A fixed size array parameter should be passed like this:
std::uint8_t(&dmac)[6]
The the rest of the example then becomes like this:
#include <cstdint>
#include <algorithm>
struct MplsTunnel_
{
MplsTunnel_(const std::uint32_t port, const std::uint32_t iflIndex, const std::uint16_t vsi, const int vcEncapAccess, const std::uint8_t(&dmac)[6]) :
_port{ port },
_iflIndex{ iflIndex },
_vsi{ vsi },
_vcEncapAccess{ vcEncapAccess }
{
std::copy(std::begin(dmac), std::end(dmac), std::begin(_dmac));
}
std::uint32_t _port;
std::uint32_t _iflIndex;
std::uint16_t _vsi;
int _vcEncapAccess;
std::uint8_t _dmac[6];
};
and then you can initialize a tunnel like this :
int main()
{
MplsTunnel_ tunnel(80, 4u, 0, 0, { 32, 20, 10, 12, 24, 33 });
}
Note replacing _dmac with a std::array type will make the code cleaner :
#include <array>
#include <cstdint>
struct mpls_tunnel_t
{
mpls_tunnel_t(const std::uint32_t port, const std::uint32_t iflIndex, const std::uint16_t vsi, const int vcEncapAccess, const std::array<std::uint8_t,6>& dmac) :
_port{ port },
_iflIndex{ iflIndex },
_vsi{ vsi },
_vcEncapAccess{ vcEncapAccess },
_dmac{ dmac }
{
}
std::uint32_t _port;
std::uint32_t _iflIndex;
std::uint16_t _vsi;
int _vcEncapAccess;
std::array<std::uint8_t, 6> _dmac;
};
int main()
{
mpls_tunnel_t tunnel(80, 4u, 0, 0, { 32, 20, 10, 12, 24, 33 });
}
A little bit orthogonal to the question as asked, but std::array<> are generally preferable to arrays and let you treat them as you would any other value type:
struct MplsTunnel_
{
uint32_t _port;
uint32_t _iflIndex;
uint16_t _vsi;
int _vcEncapAccess;
std::array<uint8_t, 6> _dmac;
MplsTunnel_(const std::array<uint8_t, 6>& dmac)
: _port(0),
_iflIndex(0),
_vsi(0),
_vcEncapAccess(0),
_dmac{dmac} {}
};
However, in the specific case asked by OP, still list-initializing the array members in the initialization list works just fine:
typedef struct MplsTunnel_
{
uint32_t _port;
uint32_t _iflIndex;
uint16_t _vsi;
int _vcEncapAccess;
uint8_t _dmac[6];
MplsTunnel_() : _port(0), _iflIndex(0), _vsi(0), _vcEncapAccess(0), _dmac{1, 2, 3, 4, 5, 6} {}
} MplsTunnel_t;
Consider the following piece of code:
#include <iostream>
using namespace std;
int main()
{
int x = 3;
const int i[] = { 1, 2, 3, 4 };
float f[i[3]];
struct S { int i, j; };
const S s[] = { { 1, 2 }, { 3, 4 } };
double d[s[1].j];
}
It runs without error. However, the following:
#include <iostream>
using namespace std;
int x = 3;
const int i[] = { 1, 2, 3, 4 };
float f[i[3]]; // error: array bound is not an integer constant before ']' token|
struct S { int i, j; };
const S s[] = { { 1, 2 }, { 3, 4 } };
double d[s[1].j]; // error: array bound is not an integer constant before ']' token|
int main()
{
return 0;
}
Does not, as it gets the errors that are highlighted as comments. Can anyone explain to me why that is?
More than likely, the reason the compiler allows it within the function is due to a compiler extension: variable-length arrays. They allow arrays declared inside of functions to have non-constexpr lengths. But it only works inside of functions, not at global scope.
You have to use constexpr instead of const
constexpr int i[] = { 1, 2, 3, 4 };
constexpr S s[] = { { 1, 2 }, { 3, 4 } };
const applies for variables, and prevents them from being modified in your code.
constexpr tells the compiler that this expression results in a compile time constant value, so it can be used in places like array lengths, assigning to const variables, etc.
Reason why it does compile in funcion is VLA. Its not possible to declare VLA in global scope.
6.7.6.2 Array declarators
2 If an identifier is declared as having a variably modified type, it
shall be an ordinary identifier (as defined in 6.2.3), have no
linkage, and have either block scope or function prototype scope. If
an identifier is declared to be an object with static or thread
storage duration, it shall not have a variable length array type.
Also VLAs are not part of c++ standard, its only compiler extension here.
I need to declare a class member array of structs that is ideally initialized at declaration like this:
class Foo
{
typedef struct _TMember
{
uint16 m_key;
uint16 m_val;
}
TMember;
TMember m_member_tab[] =
{
{ 10, 2400},
{ 20, 2500},
{ 30, 2600},
{ 40, 2700},
{ 50, 2650},
};
// etc...
};
Can this be done in traditional C++ (pre C++11) class header?
Edit: If not, what would be a good alternative? It'd be good to have the array as a class member, but otherwise it can be defined in a common header file.
Static class variables used to have to be initialized in the implementation file, outside of the header.
With C++11, in-class initialization works if the static class variables is constexpr:
#include <iostream>
class Foo{
public:
struct TMember //can't start with an underscore; typedef unecessary in C++
{
unsigned short m_key;
unsigned short m_val;
};
constexpr static TMember m_member_tab[]={
{ 10, 2400},
{ 20, 2500},
{ 30, 2600},
{ 40, 2700},
{ 50, 2650},
};
};
int main()
{
using namespace std;
cout<<Foo::m_member_tab[1].m_val<<endl;
}
Old C++:
#include <iostream>
class Foo{
public:
struct TMember //can't start with underscore; typedef unecessary in C++
{
unsigned short m_key;
unsigned short m_val;
};
static TMember m_member_tab[5];
};
//End of header, start of implementation
Foo::TMember Foo::m_member_tab[] = {
{ 10, 2400},
{ 20, 2500},
{ 30, 2600},
{ 40, 2700},
{ 50, 2650},
};
int main()
{
using namespace std;
cout<<Foo::m_member_tab[1].m_val<<endl;
}
I'm trying to initialise static pointers as arrays inside a class definition, but getting runtime errors. I've seen examples where something similar is done, but I can't get it working, and not sure why. Some example code I've tried is:
class Header
{
private:
static int *pointer;
public:
int getVal(int val);
};
Class definition:
#include "Header.h"
int* Header::pointer = new int[] {0, 1, 2, 3, 4};
int Header::getVal(int val)
{
return pointer[val];
}
main:
#include "Header.h"
#include <iostream>
int main(int argc, char ** argv)
{
Header header;
for (int i = 0; i < 5; i++)
{
std::cout << header.getVal(i);
}
}
Running this causes an error while initialising the pointer. If I run through it in the debugger, and ignore the error, I can see that the pointer is initisalised with 0 at the beginning. If I then continue to step through it I get another error saying the heap's been corrupted. Is it possible to intitialise a pointer in this way? If not, are there any suggestions on how one can initialise a member variable pointer into an array, and assign it values, inside the class definition without having to assign each element of the array individually.
You could probably get away with:
class Header
{
public:
int getVal(int valIndex);
};
and then
#include "Header.h"
static int s_vals[] = {0, 1, 2, 3, 4}; // could move this line to B
int Header::getVal(int valIndex)
{
// B
return s_vals[valIndex];
}
Considering that you know the size of the array at compile time and there is no need to advertise an implementation detail if you are providing accessors anyway.
it is possible that your compiler simply does not support braced-init-list.
If so you can rewrite your class the following way
class Header
{
private:
static int *pointer;
static int *init()
{
int *p = new int[5];
std::iota( p, p + 5, 0 );
return ( p );
}
public:
int getVal(int val);
};
And then pointer is defined the following way
int * Header::pointer = Header::init();
Say you have a file example.h
class example
{
public:
int doStuff();
};
and a file example.cpp
#include "example.h"
static const int data[] = {1, 2};
inline int doStuffImpl()
{
return data[0] + data[1];
}
int example::doStuff()
{
return doStuffImpl();
}
When I compile the equivalent on solaris using the native compiler, I get a linker error that it can't find the symbol 'data'. Is what I'm doing invalid?
No it's not, it should be
static const int data[] = {1, 2};