I am working on a problem that class A and B are declared in namespace test0 and a friend function f of class A declared in namespace test1. Function f receives a reference of a class B object as the parameter. Here is an simplified example.
namespace test0 {
class B;
}
namespace test1 {
void f(test0::B& b);
}
namespace test0 {
class A {
friend void test1::f(test0::B& b);
};
}
The code works with g++. But nvcc gives the following compilation error.
a.cu:11:22: error: ‘B’ has not been declared
friend void test1::f(test0::B& b);
^
a.cu:11:27: error: ‘void test1::f(int&)’ should have been declared inside ‘test1’
friend void test1::f(test0::B& b);
^
Could you please help me figure out what the problem is? Thank you in advance.
It is important to understand that nvcc isn't a compiler, it is a compiler driver, and in both cases, the code is compiled with gcc and the error is a gcc generated error. If you put that code in a .cc extension file and compile it via nvcc, there will no be no error.
But when compiling CUDA code (in a .cu file in this case), there are some intermediate processing stages between your code and the final g++ pass which compiles it. Under the hood, what is happening is that your code is being transformed by the CUDA C++ front end parser to this:
# 1
# 2
namespace test0 {
# 3
class B;
# 4
}
# 6
namespace test1 {
# 7
void f(test0::B & b);
# 8
}
# 10
namespace test0 {
# 11
class A {
# 12
friend void test1::f(B & b);
# 13
};
# 14
}
compare that to the original friend void test1::f(test0::B& b); and you can see that the namespace has been stripped out by the cudafe++ pass. I don't know why it has been stripped out, but that is the source of the error.
I would recommend reporting this as a bug to NVIDIA if it is a real problem in your application.
After review by the NVIDIA development team, it appears that this may be exposing a bug in the gnu compiler. It is true that the front-end processing by the nvcc toolchain creates a host code (passed to the host compiler) that drops the namespace qualification for b's type, but this should be acceptable because B has already been declared in test0 namespace.
It appears that this has already been reported to the gnu community.
As a supporting data point, clang++ 3.9.1 on Fedora 25 compiles the code reported in the answer given by #talonmies with no errors and no warnings. On my testing up through gnu 6.4.1 on Fedora25, the gnu toolchain still throws an error. I'm not claiming this as a proof point, merely a suggestion that the claim of a bug in gnu might be possibly correct. I am not a language expert. Furthermore I don't wish to start an argument here about that; it's not the purpose of this question or answer.
The NVIDIA development team has taken the issue under review and hopes to have a fix or workaround in a future CUDA release.
In the meantime, the source-level workaround suggested is to use a dummy typedef for B in class A. i.e.:
class A {
typedef B dummy_t;
friend void test1::f(dummy_t & b);
};
UPDATE:
The issue should be resolved in CUDA 10.1.105 (CUDA 10.1)
Related
I was working on a project that links to libcrafter (Version 0.2), today. When I build this project with g++ (5.4), there were no problems.
I'm working on switching to clang-8, so I set CC and CXX to clang, clang++ respectively, ran a cmake, and started the build over.
That resulted in the titular error message:
friend declaration of 'ConnectHandler' does not match any declaration in the namespace 'Crafter'
/* Rest of the error message, including the following declaration
* friend void* Crafter::ConnectHandler(void *thread_arg), the line and
* column number, filename etc.
*/
I also got the same error for another function in the code. The errors were indicated in crafter/Utils/TCPConnection.h (line 247, 248).
On checking the header file, I found the friend declarations:
namespace Crafter
{
class TCPBuffer
{
// Stuff
};
class TCPConnection
{
// Other declarations etc.
friend void* Crafter::ConnectHandler(void *thread_arg);
friend void Crafter::PckHand(Crafter::Packet *sniff_packet, void *user);
// Class declaration continues
};
}
And in the TCPConnection.cpp file, I found:
namespace Crafter
{
void* ConnectHandler(void *thread_arg);
void PckHand(Packet *sniff_packet, void *user);
}
// Intervening code
void Crafter::PckHand(Crafter::Packet *sniff_packet, void *user
{
// Do stuff
}
void* Crafter::ConnectHandler(void *thread_arg)
{
// Do stuff
}
This looked okay to me, and my g++ based build offered no complaints. I tried to reproduce this with a minimal working example, but I have been unable to succeed so far: compiles without errors.
An added note is that this error turned up not while building crafter itself, but while building another piece of code that links to it. Specifically, filename.cc had the TCPConnection.h file included from my project includes directory, and that's where the compiler pointed me.
I would greatly appreciate any help in understanding this error, and resolving it.
I've searched this site and cannot find a solution to my problem. I have reduced my sample code down as much as I think I can while still retaining the relevant error. I am left with the following two files:
test.hpp
namespace models {
template<typename FloatingPoint>
class ellipsoid {
public:
explicit ellipsoid(FloatingPoint = 6378137.0);
private:
FloatingPoint a_;
};
template<typename FloatingPoint>
ellipsoid<FloatingPoint>::ellipsoid(FloatingPoint a) : a_(a) {}
} // End namespace models
// Function declaration
template<typename FloatingPoint>
FloatingPoint compute(FloatingPoint,
const models::ellipsoid<FloatingPoint>& =
models::ellipsoid<FloatingPoint>());
// Function definition
template<typename FloatingPoint>
FloatingPoint compute(FloatingPoint x,
const models::ellipsoid<FloatingPoint>& model) {
return 3.14;
}
test.cpp
#include "test.hpp"
int main() {
compute(10.0);
return 0;
}
When I compile the above code using VC++ 2017, I get the following error:
error C2512: 'models::ellipsoid<FloatingPoint>': no appropriate default constructor available
note: No constructor could take the source type, or constructor overload resolution was ambiguous
Both clang and g++ compile this without a problem. Also, if I remove the ellipsoid class from the namespace models, and remove the models:: invocations in the compute function, it then compiles fine using VC++. Is this a bug within the VC++ compiler, or have I got a bug in my code?
This was a bug in VC++, which was fixed in Visual Studio 2019 since compiler version 19.23. Demo: https://gcc.godbolt.org/z/a5TxPa8ac
So I have this code in 2 separate translation units:
// a.cpp
#include <stdio.h>
inline int func() { return 5; }
int proxy();
int main() { printf("%d", func() + proxy()); }
// b.cpp
inline int func() { return 6; }
int proxy() { return func(); }
When compiled normally the result is 10. When compiled with -O3 (inlining on) I get 11.
I have clearly done an ODR violation for func().
It showed up when I started merging sources of different dll's into fewer dll's.
I have tried:
GCC 5.1 -Wodr (which requires -flto)
gold linker with -detect-odr-violations
setting ASAN_OPTIONS=detect_odr_violation=1 before running an instrumented binary with the address sanitizer.
Asan can supposedly catch other ODR violations (global vars with different types or something like that...)
This is a really nasty C++ issue and I am amazed there isn't reliable tooling for detecting it.
Pherhaps I have misused one of the tools I tried? Or is there a different tool for this?
EDIT:
The problem remains unnoticed even when I make the 2 implementations of func() drastically different so they don't get compiled to the same amount of instructions.
This also affects class methods defined inside the class body - they are implicitly inline.
// a.cpp
struct A { int data; A() : data(5){} };
// b.cpp
struct A { int data; A() : data(6){} };
Legacy code with lots of copy/paste + minor modifications after that is a joy.
The tools are imperfect.
I think Gold's check will only notice when the symbols have different types or different sizes, which isn't true here (both functions will compile to the same number of instructions, just using a different immediate value).
I'm not sure why -Wodr doesn't work here, but I think it only works for types, not functions, i.e. it will detect two conflicting definitions of a class type T but not your func().
I don't know anything about ASan's ODR checking.
The simplest way to detect such concerns is to copy all the functions into a single compilation unit (create one temporarily if needed). Any C++ compiler will then be able to detect and report duplicate definitions when compiling that file.
Using CUDA 5.0 on ubuntu with gcc/g++ 4.6, I'm getting errors when linking against CUDA code with templates.
cu_array.cu:
#include "cu_array.hpp"
template<class T>
CuArray<T>::CuArray(unsigned int n) {
cudaMalloc(&data,n*sizeof(T));
}
cu_array.hpp:
#pragma once
template<class T>
class CuArray {
public:
CuArray(unsigned int n);
private:
T* data;
};
main.cu:
#include "cu_array.hpp"
int main() {
CuArray<float> a(10);
}
These compile fine with nvcc -c, but linking with nvcc cu_array.o main.o gives undefined reference to CuArray<float>::CuArray(unsigned int). If I move the contents of cu_array.cu into the header and only build the main, it uses the templates just fine. Or if I remove the templates altogether, the code naturally links fine.
I'm sure there's a simple answer for this. Any ideas?
You haven't instantiated the class in the compilation unit where it is defined, so the compiler doesn't emit any code for the class member function, and linkage fails. This isn't specific to CUDA, this greedy style of instantiation is the compilation/linkage model g++ uses, and lots of people get caught out by it.
As you have found already, the simplest solution is to include everything into the same compilation unit, and the problem disappears.
Otherwise if you explicitly instantiate CuArray::CuArray at the bottom of cu_array.cu like this:
template CuArray<float>::CuArray(unsigned int);
the compiler will emit code where it would otherwise not, and the linkage problem will be fixed. You will need to instantiate every class function for every type you want to use elsewhere in the code to make this approach work.
I have been porting some C++ code, which was written long time ago, and is normally compiled with Visual C++ (Visual Studio 7.1 version) and Intel C++ Compiler 11.0, the target platform is Linux (Suse x86-64), with GCC 4.3.2 and Intel C++ Compiler 11.1
The problem is that code like this
FileA.h
template<typename T, int dim>
class A
{
public:
A(){};
~A(){};
protected:
void foo1(){};
}
FileB.h
#include "FileA.h"
template<typename T>
class B : public A<T, 2>
{
public:
B(){};
~B(){};
void foo(){ foo1(); }
}
main.cpp
#include "FileB.h"
int main()
{
B<float> b = B<float>();
}
does not compile on Linux (Intel C++ 11.1, GCC 4.3.2), but perfectly compiles on Windows (Visual C++ 7.1, Intel C++ 11.0), althow it surely must not depend on platform.
GCC tells that if I change foo1() to foo1(T a) it will work (and it does), but I can not change the code, and have to use Intel C++ for final release.
I would be glad if anyone could help with any advice.
foo1 is not a dependent expression so the base class, which is a dependent type, is not used to resolve the foo1 call.
As you can't change the code, you are stuffed. If you could change the code you would need to change the expression to be dependent. Typically this is done by changing it to this->foo1().
This is a well-known problem with templates. It is explained in the C++ FAQ
On gcc 4.4.1 (os is Ubuntu) version I could turn the compile error into a compile warning, by using the -fpermissive option to the compiler.
Edit: The fact that some compiler accept it, doesn't mean it will continue to accept it in future versions.