got compilation error while trying to compile simple cpp program
i have tried the following compilation lines
1) gcc hello.cpp -std=gnu++11
2) g++ hello.cpp -std=gnu++11
this is my cpp file
#include <iostream>
#include <map>
#include <string>
using namespace std;
static map<string,int> create_map()
{
map<string,int> mymap = {
{ "alpha", 10 },
{ "beta", 20 },
{ "gamma", 30 } };
return mymap;
}
map<string,int> m= create_map();
int main()
{
cout << "Hello, World!";
for (map<string,int> x: m)
cout << x.first << ": " << x.second << '\n';
return 0;
}
the output of gcc contain a lot of linkage error like
hello.cpp:(.text+0x135): undefined reference to `std::cout'
hello.cpp:(.text+0x13a): undefined reference to `std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
and g++ output was shorter
hello.cpp: In function 'int main()':
hello.cpp:19:29: error: conversion from 'std::pair<const std::basic_string<char>, int>' to non-scalar type 'std::map<std::basic_string<char>, int>' requested
hello.cpp:20:18: error: 'class std::map<std::basic_string<char>, int>' has no member named 'first'
hello.cpp:20:37: error: 'class std::map<std::basic_string<char>, int>' has no member named 'second'
the code is correct for 99.99%. i don't know really how to compile it properly
There are some very different kind of errors you see.
gcc hello.cpp -std=gnu++11
This tries to compile the code using the GNU c-compiler.
C++ source files are recognized and compiled regarding the c++ syntax, but the linker uses c standard libraries, and the c++ standard libraries aren't automatically linked.
There must be some difference with your source, since I can't reproduce the linker errors you're claiming with this test.
In the other case
g++ hello.cpp -std=gnu++11
your code is compiled using the c++ compiler, and
for (map<string,int> x: m)
is wrong regarding the type for x in the range based loop (which actually is pair<const string,int>).
You should simply change that to
for (const auto& x: m)
to get the code working.
x will be the type of the container element type, that is iterated over from m.
Here's a working online demo.
Replace the loop with:
for (auto const& x: m)
cout << x.first << ": " << x.second << '\n';
Alternatively, you could use:
for (std::pair<string, int> x: m)
or
for (const std::pair<string, int>& x: m)
const because you do not want to change x. & to prevent a copy of the pair into x.
In the range based loop for an std::map, the variable that iterates over the container needs to be std::pair<const Key, T>. This is defined by the value_type for the container's Member types.
Since the type of x is std::pair<const string,int> you need to use instead (after comments):
for (const std::pair<const string,int>& x: m)
const & is added so that redundant pair copies are avoided (in the newest compilers they are probably avoided any way).
for (const auto& x: m)
may alternatively be used!
EDIT: Please scroll down to the "EDIT" section at the end of the question for more recent details. I'm not editing the rest of this post to preserve history of the comments.
I have a class defined like so in a header file:
class TestClass
{
public:
TestClass() { }
~TestClass() { }
void Test();
private:
static const char * const carr[];
static const int iarr[];
};
The TestClass::Test() function just makes sure that both arrays are used so they're not optimized away - prints them to log. I won't post it here for clarity. Arrays are initialized in .cpp file.
Above case works fine, when creating an instance of this class, the addresses look like so:
t TestClass * 0x20000268
carr const char * const[] 0x8002490 <TestClass::carr>
iarr const int [] 0x800249c <TestClass::iarr>
Memory addresses beginning with 0x20... belong to RAM region, while 0x80... belong to ROM/Flash. As expected, both arrays get placed in ROM.
However if I add virtual qualifier to any function in the class, e.g. its destructor like so:
class TestClass
{
public:
TestClass() { }
virtual ~TestClass() { }
void Test();
private:
static const char * const carr[];
static const int iarr[];
};
Then the result is this:
t TestClass * 0x20000268
carr const char * const[3] 0x80024b4 <TestClass::carr>
iarr const int [1000] 0x20000270
In particular - iarr is placed in RAM which is totally not what I expected.
This file is compiled like so:
arm-none-eabi-g++ -mcpu=cortex-m7 -mthumb -mfloat-abi=soft -O0 -fmessage-length=0 -fsigned-char -ffunction-sections -fdata-sections -ffreestanding -fno-move-loop-invariants -Wall -Wextra -g3 -DDEBUG -DUSE_FULL_ASSERT -DTRACE -DOS_USE_TRACE_ITM -DSTM32F767xx -DUSE_HAL_DRIVER -DHSE_VALUE=24000000 -I../include -I../system/include -I../system/include/cmsis -I../system/include/stm32f7-hal -std=gnu++11 -fabi-version=0 -fno-exceptions -fno-rtti -fno-use-cxa-atexit -fno-threadsafe-statics -c -o "src\\main.o" "..\\src\\main.cpp"
And the linking part:
arm-none-eabi-g++ -mcpu=cortex-m7 -mthumb -mfloat-abi=soft -O0 -fmessage-length=0 -fsigned-char -ffunction-sections -fdata-sections -ffreestanding -fno-move-loop-invariants -Wall -Wextra -g3 -T mem.ld -T libs.ld -T sections.ld -nostartfiles -Xlinker --gc-sections -L"../ldscripts" -Wl,-Map,"VirtualClassTestF7.map" --specs=nano.specs -o "VirtualClassTestF7.elf" "#objs.rsp"
There are more files built in this project, related to hardware initialization. I don't include those to keep the post short.
Is there any switch that controls this behavior? I've already tried the obvious parts that I could come up with, which could have a slightest connection with the issue:
Optimization levels: O0, O1, O2, O3, Os, Ofast
Removing -ffunction-sections and -fdata-sections
Adding -fno-common
Making the array larger to go over some threshold if there was some. I've made its size 10k elements (times sizeof(uint32_t)) and it was still in RAM
Trying three different versions of toolchain
Toolchain is arm-none-eabi. Tried versions (outputs of arm-none-eabi-gcc --version):
arm-none-eabi-gcc.exe (GNU Tools for ARM Embedded Processors) 4.9.3 20150529 (release) [ARM/embedded-4_9-branch revision 224288]
arm-none-eabi-gcc.exe (bleeding-edge-toolchain) 7.2.0
arm-none-eabi-gcc.exe (bleeding-edge-toolchain) 8.3.0
Cygwin (gcc (GCC) 7.4.0)
First one comes from official ARM website: https://developer.arm.com/open-source/gnu-toolchain/gnu-rm/downloads. Last two come from http://www.freddiechopin.info/en/download/category/11-bleeding-edge-toolchain as ARM officially doesn't release 64-bit version and our project grew to size that breaks the 32-bit version.
Why this is an issue and why I'm specifically looking into a compiler switch: Possibly there is another way to force those values into ROM by writing it a bit differently. This is not an option - we've encountered this issue recently in a larger project spanning thousands of files where class inheritance is used heavily in various places. Catching all the possible occurrences of such arrays (some being created with macros, some by external tools) and then reorganizing all that code is out of the question. Therefore I'm looking for a reason why the compiler behaves in this exact way and what are the possible solutions that don't involve touching the source files.
EDIT: It appears to be some kind of issue with gdb and how it retrieves the address of that variable, or I'm missing something. I went ahead and created the same example on PC (Cygwin gcc 7.4.0):
#include <stdio.h>
class TestClass
{
public:
TestClass() { }
virtual ~TestClass() { }
static const char * const carr[];
static const int iarr[];
};
const char * const TestClass::carr[] = {
"test1", "test2", "test3"
};
const int TestClass::iarr[] = {
1,2,3,4,5,6,7,8,9,0
};
int main() {
TestClass instance;
printf("instance: %p, carr: %p, iarr: %p\n", &instance, instance.carr, instance.iarr);
fflush(stdout);
while(1);
return 0;
}
The output of the program is this:
instance: 0xffffcba8, carr: 0x100403020, iarr: 0x100403040
This is also confirmed by the map file. Relevant portion:
.rdata 0x0000000100403000 0xa0 ./src/main.o
0x0000000100403020 TestClass::carr
0x0000000100403040 TestClass::iarr
However gdb shows this:
p instance.iarr
$2 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
p &instance.iarr
[New Thread 57872.0x4f28]
$3 = (const int (*)[10]) 0x60003b8a0
p &instance.iarr
$4 = (const int (*)[10]) 0x60003b8d0
And what's even more interesting is that this address changes every time I attempt to print it with gdb. What's the reason for this?
Question title and tags adjusted.
gdb copies your array into RAM, you don't even need an instance for it, a class with a vtable is enough:
(gdb) p TestClass::iarr
$1 = {1, 2, 3, 4, 5, 6}
(gdb) p (int*)TestClass::iarr
$2 = (int *) 0x7ffff7a8b780
(gdb) p *(int *) 0x7ffff7a8b780 # 100
$3 = {1, 2, 3, 4, 5, 6, 0 <repeats 94 times>}
(gdb) p (int*)TestClass::iarr
$4 = (int *) 0x7ffff7a8b7a0
(gdb) p (int*)TestClass::iarr
$5 = (int *) 0x7ffff7a8b7c0
(gdb) p *(int *) 0x7ffff7a8b780 # 100
$6 = {1, 2, 3, 4, 5, 6, 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 1, 2, 3, 4, 5, 6, 0 <repeats 78 times>}
I guess this boils down to gdb's interpretation of "C". If you need the real address in gdb, you need a function that returns it.
I need to use an array as the map key, but I receive compiler errors indicating that the map declaration does not name a type.
I use the code in a similar question, but the code does not compile even when I have chosen the -std=c++0x or -std=c++11 compiler commands.
The code I used is:
typedef std::array<unsigned int, 3> alphabet;
std::map<alphabet, std::string> dictionary;
dictionary[{{1, 0, 8}}] = "hello";
The error is:
error: 'dictionary' does not name a type| error: expected
unqualified-id before ']' token| ||=== Build finished: 2 errors, 0
warnings (0 minutes, 1 seconds) ===|
I see little on this topic even when searching Google. I am using CodeBlocks as my IDE and chosen the compiler commands mentioned above.
I think the error may be because you're trying to assign to dictionary in file scope. As pointed out, variables should be initialized in global scope, i.e.:
std::map<alphabet, std::string> dictionary = { {{1,0,8}, "hello"} };
Otherwise, you should put it in block scope, i.e. in a main().
#include <array>
#include <map>
typedef std::array<unsigned int, 3> alphabet;
std::map<alphabet, std::string> dictionary;
int main()
{
dictionary[{{1, 0, 8}}] = "hello";
}
As a side note, it seems that the braces can be elided. You do not need two sets of braces. dictionary[{1, 0, 8}] will suffice.
How you compare the arrays for the map sort?
I guess you should supply compare method like this:
struct ltarray
{
bool operator()(const alphabet& s1, const alphabet& s2) const
{
//how you compare???
return (s1<s2);
}
};
and you need to init your map template with the compare method:
std::map<alphabet, std::string, ltarray> dictionary;
Initializing an array (in C++, but any solution which works for C will likely work here as well) with less initializers than it has elements is perfectly legal:
int array[10] = { 1, 2, 3 };
However, this can be a source of obscure bugs. Is there a way to have the compiler (gcc) check the number of initializers for one specific array, and emit a warning or even an error if declared and actual size don't match?
I know I can use int array[] = { 1, 2, 3 }; and could then use static assertions involving sizeof(array) to verify my expectation there. But I'm using array in other translation units, so I have to declare it with an explicit size. So this trick won't work for me.
(promoted from a comment as requested)
If the values in the array are important to the correct functionality of the system, and having zero-initialized values at the end causes bugs, then I would just add a unit test to verify the array contains the right data, instead of trying to enforce it in the code.
Since you are using array in other translation units, it apparently has external linkage. In this case, you are allowed to declare it twice, as long as the declarations give it the same type. So simply declare it twice, once allowing the compiler to count the initializers and once specifying the size. Put this line in one source file, before any header that declares array:
int array[] = { 1, 2, 3 };
Later in the same file, put an #include line that declares array, with a line such as:
extern int array[10];
If the array sizes differ in the two declarations, the compiler will report an error. If they are the same, the compiler will accept them.
I have an idea.
#define C_ASSERT(expr) extern char CAssertExtern[(expr)?1:-1]
#define NUM_ARGS__(X, \
N64,N63,N62,N61,N60, \
N59,N58,N57,N56,N55,N54,N53,N52,N51,N50, \
N49,N48,N47,N46,N45,N44,N43,N42,N41,N40, \
N39,N38,N37,N36,N35,N34,N33,N32,N31,N30, \
N29,N28,N27,N26,N25,N24,N23,N22,N21,N20, \
N19,N18,N17,N16,N15,N14,N13,N12,N11,N10, \
N09,N08,N07,N06,N05,N04,N03,N02,N01, N, ...) N
#define NUM_ARGS(...) \
NUM_ARGS__(0, __VA_ARGS__, \
64,63,62,61,60, \
59,58,57,56,55,54,53,52,51,50, \
49,48,47,46,45,44,43,42,41,40, \
39,38,37,36,35,34,33,32,31,30, \
29,28,27,26,25,24,23,22,21,20, \
19,18,17,16,15,14,13,12,11,10, \
9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define DECL_INIT_ARRAYN(TYPE, NAME, COUNT, N, ...) \
C_ASSERT(COUNT == N); \
TYPE NAME[COUNT] = { __VA_ARGS__ }
#define DECL_INIT_ARRAY(TYPE, NAME, COUNT, ...) \
DECL_INIT_ARRAYN(TYPE, NAME, COUNT, NUM_ARGS(__VA_ARGS__), __VA_ARGS__)
DECL_INIT_ARRAY(const int, array3_3, 3, 1, 2, 3);
int main(void)
{
DECL_INIT_ARRAY(const int, array5_4, 5, 1, 2, 3, 4);
DECL_INIT_ARRAY(const int, array5_6, 5, 1, 2, 3, 4, 5, 6);
return 0;
}
Output (ideone):
prog.c: In function ‘main’:
prog.c:33:3: error: size of array ‘CAssertExtern’ is negative
prog.c:34:3: error: size of array ‘CAssertExtern’ is negative
prog.c:34:3: error: excess elements in array initializer [-Werror]
prog.c:34:3: error: (near initialization for ‘array5_6’) [-Werror]
prog.c:34:3: error: unused variable ‘array5_6’ [-Werror=unused-variable]
prog.c:33:3: error: unused variable ‘array5_4’ [-Werror=unused-variable]
prog.c:34:3: error: unused variable ‘CAssertExtern’ [-Werror=unused-variable]
cc1: all warnings being treated as errors
UPD: The OP has found a shorter C++11 solution, building upon the same idea of using __VA_ARGS__ and a static/compile-time assertion:
#include <tuple>
#define DECL_INIT_ARRAY(TYPE, NAME, COUNT, ...) \
static_assert(COUNT == \
std::tuple_size<decltype(std::make_tuple(__VA_ARGS__))>::value, \
"Array " #NAME " should have exactly " #COUNT " initializers"); \
TYPE NAME[COUNT] = { __VA_ARGS__ }
DECL_INIT_ARRAY(const int, array3_3, 3, 1, 2, 3);
int main(void)
{
DECL_INIT_ARRAY(const int, array5_4, 5, 1, 2, 3, 4);
DECL_INIT_ARRAY(const int, array5_6, 5, 1, 2, 3, 4, 5, 6);
return 0;
}
Output (ideone):
prog.cpp: In function ‘int main()’:
prog.cpp:13:3: error: static assertion failed: Array array5_4 should have exactly 5 initializers
prog.cpp:14:3: error: static assertion failed: Array array5_6 should have exactly 5 initializers
prog.cpp:14:3: error: too many initializers for ‘const int [5]’
prog.cpp:13:3: warning: unused variable ‘array5_4’ [-Wunused-variable]
prog.cpp:14:3: warning: unused variable ‘array5_6’ [-Wunused-variable]
I looked around for a specific answer to this in C99 and found an answer here: How can I use “sizeof” in a preprocessor macro?
If you don't define the size of your array and use:
int test[] = {1,2}
STATIC_ASSERT(sizeof(test)/sizeof(test[0]) == 3)
/* with C11 support: */
_Static_assert(sizeof(test)/sizeof(test[0]) == 3)
/* or more easily */
ASSERT_ARRAY_LENGTH(test, 3);
you can easily detect if the sizeof the array is as you expected. A compilation error will be raised if the static assert fails. There is no run time overhead. A very solid implementation of the static assert can be found here:
Static assert implementation C
for your convenience:
#define ASSERT_CONCAT_(a, b) a##b
#define ASSERT_CONCAT(a, b) ASSERT_CONCAT_(a, b)
/* These can't be used after statements in c89. */
#ifdef __COUNTER__
#define STATIC_ASSERT(e,m) \
;enum { ASSERT_CONCAT(static_assert_, __COUNTER__) = 1/(int)(!!(e)) }
#else
/* This can't be used twice on the same line so ensure if using in headers
* that the headers are not included twice (by wrapping in #ifndef...#endif)
* Note it doesn't cause an issue when used on same line of separate modules
* compiled with gcc -combine -fwhole-program. */
#define STATIC_ASSERT(e,m) \
;enum { ASSERT_CONCAT(assert_line_, __LINE__) = 1/(int)(!! (e)) }
#endif
I Added a macro on top of this one specifically for validating the sizeof an array. The number of elements must exactly match the specified length:
#define ASSERT_ARRAY_LENGTH(array, length)\
STATIC_ASSERT(sizeof(array)/sizeof(array[0]) == length,\
"Array is not of expected length")
If you don't need to support C99 you can use the new C11 feature _Static_assert. More info here.
If you don't require C support, you can also rely on the c++ static assert:
std::size(test) == 3; /* C++ 17 */
(std::end(test) - std::begin(end)) == 3; /* C++ 14 */