c++ extern constant int for array size - c++

I have the following three files in my code (with most of the code removed. This is just to isolate the issue).
global.h:
//global.h
#ifndef GLOBAL_H
#define GLOBAL_H
extern const int ARRAYSIZEX;
extern const int ARRAYSIZEY;
extern const int ARRAYSIZEZ;
#endif //GLOBAL_H
global.cpp:
//global.cpp
#include "global.h"
const int ARRAYSIZEX = 5;
const int ARRAYSIZEY = 2;
const int ARRAYSIZEZ = 4;
main:
//main
#include "global.h"
using namespace std;
someType mySomeTypeArray[ARRAYSIZEX][ARRAYSIZEY][ARRAYSIZEZ];
int main(int argc, char **argv)
{
//...
}
Compiling gives me three errors at the declaration of mySomeTypeArray.
error: array bound is not an integer constant before ']' token
I want to keep my global variable and array size definitions in global.h/cpp for this application, just for organization, so that all my configuration parameters are in one place. What's the proper way to achieve what I'm trying to do?
Thanks

Your declaration is failing because array sizes need to be evaluated at compile-time and your encapsulation scheme is actually hiding the values from the compiler. This is true because compilers work on individual translation units. While compiling main.cpp your compiler sees only extern const int ARRAYSIZEX thanks to the include statement, but not the value which is visible in a separate translation unit so it can't figure out the memory layout.
While const variables can used as array sizes in some contexts, the language provides the more appropriate constexpr qualifier which comes with a set of restrictions that enforce its compile-time evaluation and suitability for array sizes. I recommend always using it when appropriate because it will point you to the error in situations such as this. In this case, you would get a compiler error because an extern constexpr declaration is ill-formed which hints at the proper solution: to hold the values for compile-time constants directly inside the header file.
global.h
constexpr int ARRAYSIZEX = ...;
constexpr int ARRAYSIZEY = ...;
constexpr int ARRAYSIZEZ = ...;
main.cpp
#include "global.h"
someType mySomeTypeArray[ARRAYSIZEX][ARRAYSIZEY][ARRAYSIZEZ];

The problem here is extern int x means "x is defined in another file, but don't worry about the particulars, all you need to know is it's an int". This is normally good enough, except when the compiler needs to know right there and then what x is.
Since that's defined in a whole other file it can't. That file must be compiled before it knows, and the result of that compilation, due to the way C++ works, can't impact the compilation of this file.
You'll need to declare that as a const int in a header if you want to share those values. extern int won't cut it.
Although this is a trivial example, there's really no reason to go down the extern road at all. Just define the values in the header file as regular const int.

Array size must be specified by an integer constant expression. A const int object can be used in an integer constant expression if and only if it declared with an initializer and that initializer is also an integer constant expression. Your ARRAYSIZE... variables do not satisfy that requirement. In main they are declared without an initializer. You cannot use ARRAYSIZE... variables as array sizes in main.
Unless you have a specific requirement to give these variables external linkage, simply declare (and define) them in the header as
const int ARRAYSIZEX = 5;
const int ARRAYSIZEY = 2;
const int ARRAYSIZEZ = 4;
These object will have internal linkage though, which is different from what your original variant attempts to do.
If really want to give them external linkage, declare them as inline extern const in the header
inline extern const int ARRAYSIZEX = 5;
inline extern const int ARRAYSIZEY = 2;
inline extern const int ARRAYSIZEZ = 4;
Since inline by itself prevents const from imposing internal linkage, extern is entirely optional in these declarations. And since inline const combination can be replaced with constexpr (as #M.M noted in the comments), you can achieve the same effect with just
constexpr int ARRAYSIZEX = 5;
constexpr int ARRAYSIZEY = 2;
constexpr int ARRAYSIZEZ = 4;

The problem here is that ARRAYSIZEX, ARRAYSIZEY and ARRAYSIZEZ are not the compile time constants. They are constants - so their values can't be changed but their values are not known to compiler.
In C++ the compilation process consists of 3 basic steps.
Preprocessing of all source files done by preprocessor.
Compilation for every translation unit (.cpp file) done by compiler. For every translation unit compiler creates an object file.
Linking of all object files done by linker. The output is an executable file.
In C++ the keyword extern for compiler means that the variable is 'somewhere' defined. The compiler doesn't know the variable's real address but by placing keyword extern it's assured that the variable really exists and the linker will be able to find its address by its name when creating the executable file.
The problem here is that compiler in step 2 wants to create the object file but it doesn't known how big the array is going to be because it doesn't know the value of these constants. Yes, linker in step 3 will finally find them when putting together all object files but it's too late for compiler. So it generates that error.
The solution is simple. Use already mentioned constexpr keyword and initialize all variables with initializers. The keyword constexpr marks compile time constants - constants that have to be initialized in initializers and are known to compiler.

Related

C++ Referencing extern const within a namespace

I am trying to use the constant int SIZE that is declared in the TaxConstants.hpp namespace TAXCONSTANTS in other places in my project. When I try to compile, I get "undefined reference to 'SIZE' everywhere that SIZE is referenced.
file TaxConstants.hpp
#ifndef TaxConstants_hpp
#define TaxConstants_hpp
namespace TAXCONSTANTS
{
extern const int SIZE = 4; // I have tried with and without extern
}
#endif //TAXCONSTANTS_HPP
main.cpp
#include <iostream>
#include "TaxConstants.hpp"
using namespace std;
using namespace TAXCONSTANTS;
int main()
{
extern const int SIZE;
// This is a struct defined in another file. It is a sample of my use for SIZE. I left out the #include above to simplify things.
taxPayer payers[SIZE];
//More code
return 0;
}
Additional info: this is a school project and my teach has made it a requirement to declare constants in the file TaxConstants.hpp in the namespace TAXCONSTANTS.
There are 5 files in total, the file with my functions is having the same undefined reference to SIZE error.
I have spent hours looking up similar explanations on the extern function and namespaces, but most of the suggestions are against doing this in the first place an offer another solution. I, unfortunately can not use them. Other errors people had were getting "multiple decorations" which I am not having.
EDIT
See Brians explanation below for better detail.
What I needed to do was define
const int SIZE = 4;
within the TaxConstants.hpp file in namespace TAXCONSTANTS.
Then remove
'extern const int SIZE;'
from my main file and instead reference SIZE by TAXCONSTANTS::SIZE everywhere I wanted to use size.
This is basic namespace stuff that I completely forgot about.
If you define SIZE without the extern keyword, it will have internal linkage since it is const. You can refer to it inmain.cpp as TAXCONSTANTS::SIZE. This is recommended since the compiler will be able to inline the value wherever SIZE is used.
If you define SIZE with the extern keyword, it will have external linkage and it should not be in a header, unless you want multiple definition errors. You should instead define it in a .cpp file which will be linked into the rest of the program. In this case there will be only one copy of SIZE in the entire program. You should avoid this approach (preferring instead the approach without extern) unless for some reason you actually need to have only one copy of SIZE in the entire program.
In both cases, SIZE will be a member of the TAXCONSTANTS namespace.
Your attempt to redeclare SIZE inside main does not do what you think it does! The following inside main:
extern const int SIZE;
actually has the effect of declaring SIZE in the global namespace. Since there is no definition of SIZE in the global namespace, you get undefined reference errors at link time. It is not the correct way to refer to the SIZE variable defined in TAXCONSTANTS.
Multiple problems with the entire approach.
Your
extern const int SIZE;
in main is a declaration of const int object SIZE from global namespace - ::SIZE. This SIZE has absolutely nothing to do with your TAXCONSTANTS::SIZE. Such global ::SIZE object is not defined in your program, which is why you get "undefined reference" error.
Since you already declared TAXCONSTANTS::SIZE in the header file, you don't need to redeclare SIZE again in main. Why are you doing this?
Just remove the declaration from main and use SIZE from TAXCONSTANTS either through using namespace TAXCONSTANTS or by specifying a qualified name TAXCONSTANTS::SIZE.
The declaration you have in the header file is actually a definition. Including this header file into multiple translation units will result in another error: multiple definitions of the same object with external linkage.
If you want to declare a global constant object, you have to keep a mere non-defining declaration in the header file
namespace TAXCONSTANTS
{
extern const int SIZE; // declaration, not definition
}
and move the definition into one of implementation files
namespace TAXCONSTANTS
{
extern const int SIZE = 4; // definition
}
However, it appears that you are planning to use this constant as an Integral Constant Expression (as array size in array declaration). An extern const int constant declared without an initializer will not work for that purpose.
Just forget about extern and declare a normal constant with internal linkage in the header file
namespace TAXCONSTANTS
{
const int SIZE = 4; // definition with internal linkage
}
and then just use in everywhere it is needed
using namespace TAXCONSTANTS;
int main()
{
taxPayer payers[SIZE];
...
}
or
// no 'using namespace TAXCONSTANTS;'
int main()
{
taxPayer payers[TAXCONSTANTS::SIZE];
...
}

extern variable and array declare issue c++

I have a problem with an extern variable and an array declaration with it.
How to declare an array with global variable which located not in the declarable file.
file1.cpp
const int size = 10;
mainfile.cpp
extern const int size;
void main()
{
int mas[size];
}
int mas[size];
This line has an issue.
Please any guess??
You can't. An array size must be a constant expression; if it's a variable, then that variable must be const and initialised in the same translation unit, so that its value is available for use as a constant.
If you want to share the value between multiple translation units, define it in a header and include that.
First of all constants have internal linkage. Thus these declarations
file1.cpp
const int size = 10;
and
mainfile.cpp
extern const int size;
refer to different entities.
The constant declared in file1.cpp is not visible outside its corresponding compilation unit.
According to the C++ Standard (3.5 Program and linkage)
3 A name having namespace scope (3.3.6) has internal linkage if it is
the name of
— a non-volatile variable that is explicitly declared const or
constexpr and neither explicitly declared extern nor previously
declared to have external linkage; or
In mainfile the value of size is not specified so the compiler will issue an error for statement
int mas[size];
becuase the size of an array shall be a compile-time constant expression.
The simplest solution is to place the constant definition
const int size = 10;
in some common headet file that will be included in each translation unit where there is a reference to the constant.
int mas[size];
This line has an issue. Please any guess??
As other users pointed, the issue could be that you're trying to create a Variable Lenght Array which is something not allowed in C++ (but almost enter in C++14 as Dynamic Arrays*).
Some compilers accept VLA as an extension (no standard) so I'm guessing that you're using one that doesn't have this extension or have this extension disabled.
Don't worry, you have workarrounds anyway...
#define (don't do that)
Assuming that the issue is the VLA, if we ensure the size as a compile-time value, the problem is solved so...
// file1.hpp <-- This is now a HEADER not a CPP
#define SIZE 10
// mainfile.cpp
#include "file1.hpp"
void main()
{
int mas[SIZE]; // accepted, equivalent to int mas[10].
}
constexpr
C++11 introduced the constexpr* keyword which can be used to achieve your goal
// file1.hpp <-- This is now a HEADER not a CPP
constexpr int size() { return 10; }
// mainfile.cpp
#include "file1.hpp"
void main()
{
int mas[size()];
}
enum
Enumerations are compile-time constants, so you can use them this way:
// file1.hpp <-- This is now a HEADER not a CPP
enum constant { size = 10 };
// mainfile.cpp
#include "file1.hpp"
void main()
{
int mas[constant::size];
}
*If someone found a better link, please let me know.
C++ does not allow the size of arrays to be specified at runtime. In your example of course it is specified at link time, but that is no help to the compiler.
If you are using a C++14 compiler however, and on certain other compilers (such as gcc) you can do this, but it is less portable than allocating the memory dynamically, and lest convenient than a std::vector<>.
For reference: https://isocpp.org/wiki/faq/freestore-mgmt#dynamic-array-len

Why multiple definition error in C++ not caused by const int declaration?

I have a header file foo.h:
#ifndef __FOO_H__
#define __FOO_H__
const char* USB_MANAGER_DBUS_SERVICE = "com.USBService";
#define USB_MANAGER_DBUS_OBJ_PATH "/com/USB/MgrObject"
const int DBUS_CONNECTION_MAX_RETRY_TIME = 5;
#endif
And many cpp files that includes foo.h.
foo.c bar.c
When compiling them together, "multiple definition error" appears.
Linking CXX shared library:
foo.cpp.o:(.data.rel.local+0x0): multiple definition of `USB_MANAGER_DBUS_SERVICE'
bar.cpp.o:(.data.rel.local+0x0): first defined here
So I have two questions below:
Why doesn't #define lead to a link error?
Why doesn't const int lead to a link error?
The #define for USB_MANAGER_DBUS_OBJ_PATH is constant across compilation units, it is a text substitution.
So is the const int for DBUS_CONNECTION_MAX_RETRY_TIME constant across TU. The const makes the variable read only, it's essentially not declaring it as a modifiable lvalue, it has an implicit internal linkage, from these posts.
const char* USB_MANAGER_DBUS_SERVICE = "com.USBService";
Why does USB_MANAGER_DBUS_SERVICE cause a linker error?
It is not const, as in the pointer is not a constant value, only what is being pointed to.
const char* const USB_MANAGER_DBUS_SERVICE = "com.USBService";
// ^^^^^ added const
Would be const.
Define doesn't cause a link error because the preprocessor is just pasting the string literal into different parts of the code instead of referring to some object defined in some object file.
const int does not cause a link error because const implies internal linkage, so you end up having a constant per compilation unit.
You can fix the redefinition error by defining extern const char* USB_MANAGER_DBUS_SERVICE; in the header file and defining const char* USB_MANAGER_DBUS_SERVICE = "..." in one of your source files.
"#define"d tokens are already processed by the preprocessor, even before the compiler comes into play.
In C++ and "const int" values are evaluated at compile time, i.e. a "const int" is equivalent to a typesafe define (and thus should be preferred over #define).
The only symbol the compiler actually creates for the linker is "USB_MANAGER_DBUS_SERVICE", because only the target to which the pointer points to is "const", but not the pointer itself.
Here's the problem. By having #ifndef FOO_H wrapper, you are avoiding multiple inclusion of foo.h in your .c or .cpp file, but when linker links all different object files together, you have multiple definitions.
Remedy is you use like the following in foo.h:
#ifndef __FOO_H__
#define __FOO_H__
extern const char* USB_MANAGER_DBUS_SERVICE;
#define USB_MANAGER_DBUS_OBJ_PATH "/com/USB/MgrObject"
extern const int DBUS_CONNECTION_MAX_RETRY_TIME;
#endif
And, in only one .c or .cpp file (i.e one source file), use the following:
const char* USB_MANAGER_DBUS_SERVICE = "com.USBService";
const int DBUS_CONNECTION_MAX_RETRY_TIME = 5;
This will solve your multiple definition problem complained by the linker.
const char* USB_MANAGER_DBUS_SERVICE = "com.USBService";
The string this pointer points to is const, but the pointer is not const, thus it is a multiple definition. Change it to
const char USB_MANAGER_DBUS_SERVICE[] = "com.USBService";
There is no linkage error for the const int because it is a real constant, not a const-modifier. It is therefore considered static data to the application.
Incidentally if you put in another const for the pointer
const char* const USB_MANAGER_DBUS_SERVICE = "com.USBService";
Now that pointer is a proper constant too, but I'm not sure if it will work. You can declare it as extern and then define it in one compilation unit, but the simplest is the first syntax I showed you.

How to access global const variable present in one file to different file

This variable is defined in xyz.cpp
const int i = 10;
The below main() method is written in abc.cpp
void main()
{
cout<<i; //trying to access 'i' from a different .cpp file
}
You need to declare it extern in abc.cpp:
extern const int i;
EDIT: As stated in the comment below, in C++ (unlike C), to give a const variable external linkage, you need to declare it es extern also in xyz.cpp:
extern const int i = 10;
Reference: MSDN
The simplest way is to declare it in a header as const static int i=10; and include that header in all the .cpp that need that constant. This will allow it to be "inlined" by the compiler in every object file, still avoiding ODR violations.
---edit---
Actually, in C++ the static is not required, since const objects have internal linkage by default. But in C, you have to put that static (and in C++ it doesn't hurt).
Why is this better than the extern method? Because:
it's more idiomatic. In C++ you'll always write integral constants in headers as const variables (and that's the very reason why they have internal linkage by default).
it's less typing. You just have to write the const declaration once, then your constants are just a #include away.
it (usually) produces more efficient code. The definition of extern-declared variables cannot be accessed by the compiler when it is producing the current object module, so it cannot optimize it by putting its actual value as an immediate value in the produced machine code, it cannot eliminate branches that cannot be reached with that value of the constant, ... Sure, cross-module optimization ("link time code generation", "whole program optimization", ...) does exist, but at the moment compilers have more advanced optimizers than linkers.
Add the keyword extern in front of it.
Declare it in abc.cpp as:
extern const int i = 10;
const variables in C++ have an internal linkage in C++(unlike C). So to be able to use it in another file you have to explicitly declare it as having external linkage.
Put this global variable into header and #include this header in all .cpp files where you want to use it.
xyz.h:
const int i = 10;
xyz.cpp:
#include "xyz.h"
void main()
{
cout << i;
}
Or if you want to avoid using header for this purpose, you can declare it as extern const int i = 10; in xyz.cpp, but then you have to declare this variable in abc.cpp too, so you should write extern const int i; at the beginning of abc.cpp in order to use it.
The same way as using extern variables.
In the file xyz.cpp:
extern const int i = 333;
In the file abc.cpp:
extern const int i;
cout << i;

Question about variables with combination of extern and const

I googled const + extern on the internet, but it seems there isn't really a good answer for my question.
const alone means internal linkage, but if I want to share a const variable among compilation units. Is extern the best choice?
Common solution would be:
//g.h
extern const int MAX;
// g.c
extern const int MAX = 3;
However, this solution has a drawback, like below:
// Say, I want to use this MAX in the same header file.
// g.h
extern const int MAX;
class AClass
{
public:
AClass (): i(MAX){}
private:
int i;
};
Compiler will complain like:"error C2057: expected constant expression".
Is there a solution for this?
if you want to be able to use your constant at compile time (i.e. size an array by it, without using VLA) it has to be known at compile time, i.e. it cannot have external linkage.
However, you could just declare your constant in your header file, and make it available to anyone including it. Still, that won't have the exact same effect as an external linkage.
// a.h
const int MAX = 3;
// a.cpp
#include "a.h"
int b[a];
An easy solution for constant integers is to use enums:
// g.h
enum { MAX = 3; }
// g.c
#include "g.h"
static char buf[MAX];
You won't be able to take the address of MAX, but in turn you get this at zero memory cost.
extern const int MAX;
int i[MAX];
Can't be done. You could do something like
const int MAX = ReadAnIntegerFromTheConsole();
Perfectly valid and legal, but whoopsies- not a constant expression.
const alone means internal linkage
This is not correct, static indicates internal linkage, const just says the object cannot mutate. Try declaring a variable as
extern static int foo;
Your compiler will complain about conflicting linkage. To share a const between translation units do exactly what you've suggested.
In the header
extern const int MAX;
In the source file
const int MAX = 10; // note you can omit the extern here
Here is a working example that may solve your issue. In summary, define the array size as a constant in a header file. In another header file declare the array as extern. In the example below I reference the array as extern without using an include file for the array.
array_size.hpp
const unsigned int MAX_ARRAY_SIZE = 16;
array.cpp
#include "array_size.hpp"
int array[MAX_ARRAY_SIZE];
main.cpp
#include "array_size.hpp"
// Reference the array from array.cpp
extern int array[MAX_ARRAY_SIZE];
int main(void)
{
array[1] = 7;
return 0;
}
The *array_size.hpp* file defines the size, the identifier can be used in other translation units by including the header.
I compiled on Cygwin using:
g++ -I. -o array.exe main.cpp array.cpp
Why not just use a #define?
#define MAX 3