I am hoping to receive some input on an issue I encountered while attempting to learn c++20 modules.
In short, I would like a namespace containing const and/or constexpr variables to be implemented within a module, and import that module into any implementation files applicable.
It works fine with non const / non constexpr variables, however this isn't ideal; I'd like to stick with const and/or constexpr depending on the data types within the namespace.
Please see example below:
// test_module.cpp
module;
#include <string_view>
export module test_module;
export namespace some_useful_ns {
/* produces a compile error when attempting to compile main.cpp */
constexpr std::string_view str_view{"a constexpr string view"};
const char* const chr{"a const char* const"};
/* compiles just fine, but not ideal; prefer const / constexpr
std::string_view str_view{"a non constexpr str view"};
const char* chr{"a const char*"};
*/
}
// main.cpp
#include <iostream>
import test_module;
int main() {
std::cout << some_useful_ns::str_view << std::endl;
std::cout << some_useful_ns::chr << std::endl;
return 0;
}
Compile the two files like below:
g++ -c --std=c++2a -fmodules-ts test_module.cpp
g++ -c --std=c++2a -fmodules-ts main.cpp
g++ main.o test_module.o
Before linking the .o files, I receive the following error when compiling main.cpp like above:
main.cpp: In function ‘int main()’:
main.cpp:6:38: error: ‘str_view’ is not a member of ‘some_useful_ns’
6 | std::cout << some_useful_ns::str_view << std::endl;
| ^~~~~~~~
main.cpp:7:38: error: ‘chr’ is not a member of ‘some_useful_ns’
7 | std::cout << some_useful_ns::chr << std::endl;
|
I find this strange since it works fine when I use a non const / non constexpr like the lines commented out in test_module.cpp.
Also, using the const / constexpr works just as expected when using a traditional implementation without modules.
Anyone have a clue as to why I am unable to get this to work successfully with const / constexpr?
In case it helps, I am using gcc (GCC) 11.2.1 20220127
Thanks in advance.
This seems like a compiler bug.
Normally, const-qualified variables have internal linkage, and names with internal linkage cannot be exported. However... there's a specific carveout for such variables which are exported. That is, exported const-qualified variables don't have internal linkage.
Related
Modern C++ offers constexpr and std::string_view as a convenient alternative to string literals. However, I am unable to link to a "constexpr std::string_view" within a module. By contrast, I am able to use string_view (not constexpr) within the module as well as a "constexpr std::string_view" outside of the module. Furthermore, the problem does not occur for other uses of constexpr within the module, such as for integers.
Below is the minimal code to recreate the error:
module interface unit (my_string.cpp):
export module my_string;
import <string_view>;
export namespace my_string {
struct MyString {
static std::string_view string_at_runtime;
static constexpr std::string_view string_at_compilation{"Hello World at compilation (inside module)"};
static constexpr int number_at_compilation{1};
};
}
module implementation unit (my_string_impl.cpp):
module;
module my_string;
namespace my_string {
std::string_view MyString::string_at_runtime = "Hello World at runtime";
}
hello_world.cpp:
import <iostream>;
import <string_view>;
import my_string;
static constexpr std::string_view hello_world{"Hello World at compilation (outside module)"};
int main(){
std::cout << hello_world << std::endl;
std::cout << my_string::MyString::string_at_runtime << std::endl;
std::cout << my_string::MyString::number_at_compilation << std::endl;
std::cout << my_string::MyString::string_at_compilation << std::endl; //<-- ERROR is here
}
Compilation and attempt to link (using gcc 11.2.0 running on Linux):
g++ -c -fmodules-ts -std=c++20 -xc++-system-header iostream string_view
g++ -c my_string.cpp -fmodules-ts -std=c++20
g++ -c my_string_impl.cpp -fmodules-ts -std=c++20
g++ -c hello_world.cpp -fmodules-ts -std=c++20
g++ -o main my_string.o my_string_impl.o hello_world.o -fmodules-ts -std=c++20
Only the last instruction (linking) results in an error:
g++ -o main my_string.o my_string_impl.o hello_world.o -fmodules-ts -std=c++20
/usr/bin/ld: hello_world.o: in function `main':
hello_world.cpp:(.text+0x97): undefined reference to `my_string::MyString::string_at_compilation'
collect2: error: ld returned 1 exit status
In addition to searching the answers to related questions (e.g. constexpr in modules and constexpr and namespaces) I have reread the GCC Wiki. I have not yet tried this code with other compilers (e.g. clang, msvc).
Is this error a failure in my code or a not-yet-implemented feature in gcc?
I have found a work around solution: adding a getter method.
In the module interface unit (my_string.cpp) add:
static std::string_view GetStringAtCompilation();
In the module implementation unit (my_string_impl.cpp) add:
std::string_view MyString::GetStringAtCompilation(){
return string_at_compilation;
}
And now the following line in the "main" function (see hello_world.cpp in question) compiles, links and executes without error:
std::cout << my_string::MyString::GetStringAtCompilation() << std::endl;
I believe that the reasons that the original attempt did not work without a getter method is given in this answer about constexpr and string_view in headers
I noticed a strange behavior when I was working with a constexpr function.
I reduced the code to a simplified example.
Two functions are called from two different translation units (module A and B).
#include <iostream>
int mod_a();
int mod_b();
int main()
{
std::cout << "mod_a(): " << mod_a() << "\n";
std::cout << "mod_b(): " << mod_b() << "\n";
std::cout << std::endl;
return 0;
}
The modules look similar. This is mod_a.cpp:
constexpr int X = 3;
constexpr int Y = 4;
#include "common.h"
int mod_a()
{
return get_product();
}
Only some internal constants differ. This is mod_b.cpp:
constexpr int X = 6;
constexpr int Y = 7;
#include "common.h"
int mod_b()
{
return get_product();
}
Both modules use a common constexpr function which is defined in "common.h":
/* static */ constexpr int get_product()
{
return X * Y;
}
I was very astonished that both functions return 12. Due to the #include directive (which should only be some source code inclusion), I supposed that there is no interaction between both modules.
When I defined get_product also to be static, the behavior was as expected:
mod_a() returned 12,
mod_b() returned 42.
I also looked Jason Turner's episode 312 of C++ Weekly: Stop Using 'constexpr' (And Use This Instead!) at https://www.youtube.com/watch?v=4pKtPWcl1Go.
The advice to use generally static constexpr is a good hint.
But I still wonder if the behavior which I noticed without the static keyword is well defined. Or is it UB? Or is it a compiler bug?
Instead of the constexpr function I also tried a C-style macro #define get_product() (X*Y) which showed me also the expected results (12 and 42).
Take care
michaeL
This program ill-formed: X and Y have internal linkage since they are const variables at namespace scope. This means that both definitions of constexpr int get_product() (which is implicitly inline) violate the one definition rule:
There can be more than one definition in a program of each of the following: [...], inline function, [...], as long as all the following is true:
[...]
name lookup from within each definition finds the same entities (after overload-resolution), except that
constants with internal or no linkage may refer to different objects as long as they are not odr-used and have the same values in every definition
And obviously these constants have different values.
What's happening is both mod_a and mod_b are calling get_product at runtime. get_product is implicitly inline, so one of the definitions is chosen and the other is discarded. What gcc seems to do is take the first definition found:
$ g++ mod_a.cpp mod_b.cpp main.cpp && ./a.out
mod_a(): 12
mod_b(): 12
$ g++ mod_b.cpp mod_a.cpp main.cpp && ./a.out
mod_a(): 42
mod_b(): 42
$ g++ -c mod_a.cpp
$ g++ -c mod_b.cpp
$ g++ mod_a.o mod_b.o main.cpp && ./a.out
mod_a(): 12
mod_b(): 12
$ g++ mod_b.o mod_a.o main.cpp && ./a.out
mod_a(): 42
mod_b(): 42
It's as if get_product isn't constexpr, since it is getting called at runtime.
But if you were to enable optimisations (or force get_product() to be called at compile time, like with constexpr int result = get_product(); return result;), the results are as you would "expect":
$ g++ -O1 mod_a.cpp mod_b.cpp main.cpp && ./a.out
mod_a(): 12
mod_b(): 42
(Though this is still UB, and the correct fix is to make the functions static)
This code violates the One Definition Rule (language lawyers please correct me if I'm wrong).
If I compile the code separately, I get the behavior that you expect:
g++ -O1 -c main.cpp
g++ -O1 -c mod_a.cpp
g++ -O1 -c mod_b.cpp
g++ *.o
./a.out
> mod_a(): 12
> mod_b(): 42
If I compile all at once or activate link-time optimization, the UB becomes apparent.
g++ -O1 *.cpp
./a.out
> mod_a(): 12
> mod_b(): 12
How to fix this
You are on the right track with declaring them static. More C++-esce would be an anonymous namespace. You should also declare the constants static or put them in a namespace, not just the function.
mod_a.cpp:
namespace {
constexpr int X = 3;
constexpr int Y = 4;
}
#include "common.h"
int mod_a()
{
return get_product();
}
common.h:
namespace {
constexpr int get_product()
{
return X * Y;
}
} /* namespace anonymous */
Even better, in my opinion: Include the common.h within an opened namespace. That makes the connection between the declarations more apparent and would allow you to have multiple public get_products, one per namespace. Something like this:
mod_a.cpp:
namespace {
constexpr int X = 3;
constexpr int Y = 4;
#include "common.h"
} /* namespace anonymous */
int mod_a()
{
return get_product();
}
In the following code, I create a Builder template, and provide a default implementation to return nothing. I then specialize the template with int, to return a value 37.
When I compile with -O0, the code prints 37, which is the expected result. But when I compile using -O3, the code prints 0.
The platform is Ubuntu 20.04, with GCC 9.3.0
Can anyone helps me understand the behavior?
builder.h
class Builder {
public:
template<typename C>
static C build() {
return 0;
}
};
builder.cc
#include "builder.h"
template<>
int Builder::build<int>() {
return 37;
}
main.cc
#include "builder.h"
#include <iostream>
int main() {
std::cout << Builder::build<int>() << '\n';
}
makefile
CXX_FLAG = -O0 -g
all:
g++ $(CXX_FLAG) builder.cc -c -o builder.o
g++ $(CXX_FLAG) main.cc builder.o -o main
clean:
rm *.o
rm main
You should add a forward declaration for build<int>() to builder.h, like so:
template<>
int Builder::build<int>();
Otherwise, while compiling main.cc, the compiler sees only the generic template, and is allowed to inline an instance of the generic build() function. If you add the forward declaration, the compiler knows you provided a specialization elsewhere, and will not inline it.
With -O3, the compiler tries to inline, with -O0 it will not inline anything, hence the difference.
Your code actually violates the "One Definition Rule": it will create two definitions for Builder::build<int>(), but they are not the same. The standard says the result is undefined, but no diagnostics are required. That is a bit unfortunate in this case, as it would have been helpful if a warning or error message was produced.
I have code in C that compiles and works correctly and I would like to use similar code in C++:
static const char* aTable[12] = {
[4]="seems",
[6]=" it ",
[8]="works",};
int main(){
printf("%s%s%s", aTable[4],aTable[6],aTable[8]);
return 0;
}
Now if I put it in a .c file and compiles with gcc it works. But, if I put it in a .cpp file and compile it with g++, I get the following errors:
test_cpp.cpp:5:3: error: expected identifier before numeric constant
test_cpp.cpp:5:4: error: type '<lambda>' with no linkage used to declare function 'void<lambda>::operator()() const' with linkage [-fpermissive]
test_cpp.cpp: In lambda function: test_cpp.cpp:5:5: error: expected '{' before '=' token
test_cpp.cpp: At global scope: test_cpp.cpp:5:5: warning: lambda expressions only available with
-std=c++0x or -std=gnu++0x [enabled by default]
test_cpp.cpp:5:6: error: no match for 'operator=' in '{} = "seems"' test_cpp.cpp:5:6: note: candidate is: test_cpp.cpp:5:4: note: <lambda()>&<lambda()>::operator=(const<lambda()>&)
test_cpp.cpp:5:4: note: no known conversion for argument 1 from 'const char [6]' to 'const<lambda()>&'
test_cpp.cpp:6:3: error: expected identifier before numeric constant
test_cpp.cpp:6:4: error: type '<lambda>' with no linkage used to declare function 'void<lambda>::operator()() const' with linkage [-fpermissive]
Is there a way to express that I am not declaring a lambda function, just trying to fill a table?
I would like to keep the following part :
[4]="seems",
[6]=" it ",
[8]="works",
because it comes from an autogenerated file...
You can mix C and C++ code easily.
You should keep the C code to be compiled with C compiler (gcc), rest of the code can be C++ and be compiled with C++ compiler (g++). then link all object (.o) files together.
like this:
file name: a.c
const char* aTable[12] = {
[4]="seems",
[6]=" it ",
[8]="works",};
file name: b.cpp
#include <cstdio>
extern "C" const char* aTable[12];
int main(){
printf("%s%s%s", aTable[4],aTable[6],aTable[8]);
return 0;
}
Now compile:
gcc -c a.c -o a.o
g++ -c b.cpp -o b.o
g++ b.o a.o -o all.out
Now run the executable (all.out) and you'll see that everything will work.
Just note that for functions you'll need to add extern "C" before the declaration in the cpp file.
There's no way to do this. C++ never adopted this particular C syntax.
In your case, since the table is auto-generated, I would just put it in a C file. As long as it's not marked static, it can be accessed from C++ code without problems.
[4]= is a designated initializer, one of the C features not supported by C++.
Here is a list that tries to list which C99 features that are supported by C++17. Scroll down to the bottom to see which ones that are not supported.
It is important to realize that C++ is by no means a superset of C.
Designated initialisers not being supported by C++ already mentioned – if you insist on C++, you could write a wrapper class with a constructor instead:
class Table
{
std::array<char const*, 12> data;
public:
Table()
{
data[4] = "seems";
data[6] = " it ";
data[8] = "works";
}
char const* operator[](unsigned int index) const
{
return data[index];
}
} aTable;
Did not test the code, so if you find a bug, feel free to fix it yourself...
As said above, this type of initialization is not supported in C++, but you could use lambda function to initialize static array<const char*, 12> like this:
#include <array>
#include <cstdio>
static const std::array<const char*, 6> aTable = []() {
std::array<const char*, 6> table;
table[0] = "Hello";
table[3] = ", ";
table[5] = "world!";
return std::move(table);
}();
int main() {
printf("%s%s%s\n", aTable[0], aTable[3], aTable[5]);
return 0;
}
Demo on coliru
Notable, that compiler will do RVO here, and the initialization will be performed in-place. This is assembly generated by g++-5.4.0:
<_GLOBAL__sub_I_main>:
movq $0x40064c,0x200bc5(%rip) # 601060 <_ZL6aTable>
movq $0x400652,0x200bd2(%rip) # 601078 <_ZL6aTable+0x18>
movq $0x400655,0x200bd7(%rip) # 601088 <_ZL6aTable+0x28>
retq
I'm trying to compile a simple program utilizing literals from the std::literals namespace, but Clang is generating errors when I try to compile it.
The code I'm trying to compile:
#include <string>
#include <iostream>
using namespace std::literals;
int main()
{
std::cout << "Hello World!"s << std::endl;
return 0;
}
and the compilation command:
clang++ -stdlib=libstdc++ -std=c++1y a.cpp
which leads to this output:
a.cpp:4:22: error: expected namespace name
using namespace std::literals;
~~~~~^
a.cpp:8:29: error: no matching literal operator for call to 'operator "" s' with arguments of
types 'const char *' and 'unsigned long', and no matching literal operator template
std::cout << "Hello World!"s << std::endl;
^
2 errors generated.
Using g++ or libc++ are out of the question for various reasons, and I've confirmed that other C++14 features (ie. return type deduction and binary literals) work, so it's not an issue with the compiler, making me believe it involves libstdc++.
What can I do to fix this? I'm on Linux Mint 17.1 if it makes any difference.
Remember to ensure that you're compiling the source according to C++14 (the chrono literals are not provided in C++11).
clang++ -stdlib=libstdc++ -std=c++14 a.cpp