extern declared template specialized function not found - c++

i'm trying to implement a clone of the json serialization library nlohmann::json as a learning experience, and i'm having trouble with the interface for user defined (json<->User type) conversion.
Basically i want the user to be able to overload two function: to_json(json&, const Type&) and from_json(const json&, Type&). Then the library will use overload resolution to call theses function in the templated operator= and one argument constructor.
It works fine when i'm just defining theses function directly but when i try to make a template definition for multiple types (in this example the class S) the linker can't find the definition.
I've tried to explicitly instantiate the function for individual instances of the templated class although i would prefer avoiding having to do that in the final product.
I'm guessing it has to do with the fact that templated function don't have the same signature than free function, but i don't see what i can do to make it work. What am i missing ? I also couldn't find result on google so is it a documented pattern or an anti pattern ?
Thanks you. Below i tried to minimize my problem in one short example.
Class.hpp
#pragma once
#include <cstdio>
template<size_t i>
class S {
size_t n = i;
};
template<size_t i>
void g(const S<i>& s) {
printf("S<%u>\n", i);
}
Class.cpp
#include "Class.hpp"
template void g<10>(const S<10>&); // <-- Even with explicitly instanciation
void g(const bool& b) {
printf("%s\n", b ? "true" : "false");
}
main.cpp
#include "Class.hpp"
template<typename T>
void f(T t) {
extern void g(const T&);
g(t);
}
int main(int, char**) {
S<10> s;
//f(s); <-- linker error: void g(class S<10> const &) not found.
f(false);
}

The name lookup for g in g(t) call stops as soon as it finds extern void g(const T&); declaration; it never sees the declaration of the function template. So the compiler generates a call to a regular non-template function named g taking const S<10>&. But no such function is defined in your program - hence linker error.

Related

Class A member template function declared as friend in class B can't access private members of class A (Clang only)

Please take a look to this code snippet. I know it does not make much sense, it is just intended to illustrate the problem I am encountering:
#include <iostream>
using namespace std;
struct tBar
{
template <typename T>
void PrintDataAndAddress(const T& thing)
{
cout << thing.mData;
PrintAddress<T>(thing);
}
private:
// friend struct tFoo; // fixes the compilation error
template <typename T>
void PrintAddress(const T& thing)
{
cout << " - " << &thing << endl;
}
};
struct tFoo
{
friend void tBar::PrintDataAndAddress<tFoo>(const tFoo&);
private:
int mData = 42;
};
struct tWidget
{
int mData = 666;
};
int main()
{
tBar bar;
bar.PrintDataAndAddress(tWidget()); // Fine
bar.PrintDataAndAddress(tFoo()); // Compilation error
return 0;
}
The code above triggers the following error:
source_file.cpp:10:3: error: 'PrintAddress' is a private member of 'tBar'
PrintAddress(thing);
source_file.cpp:42:6: note: in instantiation of function template >specialization 'tBar::PrintDataAndAddress' requested here
bar.PrintDataAndAddress(tFoo()); // Compilation error
source_file.cpp:17:7: note: declared private here
void PrintAddress(const T& thing)
but only in Clang++. GCC and MSVC are fine with it (you can quickly test that by pasting that code in http://rextester.com/l/cpp_online_compiler_clang)
It seems as if tBar::PrintDataAndAddress<tFoo>(const tFoo&) is using the same access as tFoo, where it is befriended. I know this because befriending tFoo in tBar fixes this issue. The problem also goes away if tBar::PrintDataAndAddress is a non-template function.
I have not been able to find anything in the Standard that explains this behavior. I believe it could be a bad interpretation of 14.6.5 - temp.inject, but I can't claim I have read all of it.
Does anyone know if Clang is right failing to compile the above code? Can you please quote the relevant C++ standard text if that is the case?
It seems that for this problem to happen, the private member being accessed needs to be a template function. e.g., in the example above, if
we make PrintAddress a non-template function, the code will compile without errors.
Forcing the compiler to instantiate tBar::PrintDataAndAddress<tFoo> before using it solves the problem.
int main()
{
tBar bar;
bar.PrintDataAndAddress(tWidget()); // Fine
auto x = &tBar::PrintDataAndAddress<tFoo>; // <= make it work....
bar.PrintDataAndAddress(tFoo()); // Now fine
return 0;
}
It seems to be a compiler promlem as it looks quite similar to this:
In C++, why isn't it possible to friend a template class member function using the template type of another class?
To be a little bit more precise... In the line bar.PrintDataAndAddress(tFoo()); the compiler has to instanciate the memberfunction tBar::PrintDataAndAddress<tFoo> and at the same time it has to resolve the friend declaration. That are internaly two seperate steps. Apparent the compiler doesn't do it in the rigth order when written in one expression. To force the compiler to instantiate bar.PrintDataAndAddress(tFoo()) first by access to the function pointer these two steps are in the right order.
Try adding this before the friend function
template <typename tFoo>

Overload for undefined type

I am trying to do some overload on the template function, following are the examples
do_something.h
template<typename T>
void do_something(T const &input){/*....*/}
void do_something(std::string const &input);
void do_something(boost::container::string const &input);
so far, so good, but what if I want to overload a non-defined type?
like using a type some_type have not defined in the header file
void do_something(some_type const &input);
I want to use it like this
main.cpp
#include "do_something.h"
#include "some_type.h"
#include <boost/container/string.hpp>
int main()
{
do_something(std::string("whatever"));
do_something(boost::container::string("whatever"));
//oops, some_type() never defined in the header file, this
//function will call the template version, but this is not
//the behavior user expected
do_something(some_type());
}
Since some_type is not a POD, not a std::string, boost::container::string.I guess I could designed a traits to do some compile time checking
template<typename T>
typename boost::enable_if<is_some_type<T>::value, T>::type
do_something(T const &input){//.....}
But do I have a better way to do it?
I need compile time type checking, so I use template.All of the types calling this function will do similar jobs based on different types, so I prefer overload.I do not need to save the state, so I prefer function rather than class.
Hope this could help you know more about what I intent to do.Thank you
but what if I want to overload a non-defined type?
You need to provide the declaration of
void do_something(some_type const &input);
before you call do_something with an object of type some_type. Otherwise, the template version will be used.
#include "do_something.h"
#include "some_type.h"
// This is all you need. You can implement the function here
// or any other place of your choice.
void do_something(some_type const &input);
#include <boost/container/string.hpp>
int main()
{
do_something(std::string("whatever"));
do_something(boost::container::string("whatever"));
//oops, some_type() never defined in the header file, this
//function will call the template version, but this is not
//the behavior user expected
do_something(some_type());
}

Use arbitrary functions as template parameter arguments

I have an Apache module (.so) that contains a class I'm trying to completely decouple from Apache itself. The biggest source of frustration is the debug logging. I want to be able to pass the logging function to the class through the template parameters. I can get the proof of concept to work fine when everything is in the same translation unit, but it falls over once they're not because the logging function is an 'undefined reference':
/tmp/ccPdPX2A.o: In function `main':
test.cpp:(.text+0x81): undefined reference to `void C::DoThis<&(LogIt(char const*, ...))>()'
collect2: ld returned 1 exit status
This also happens when Apache tries to load the module containing the class.
The code below reproduces the problem:
// main.cpp
#include <iostream>
#include "C.h"
void LogIt(const char*, ...)
{
std::cout << "GADZOOKS!\n";
}
int main(int argc, char* argv[])
{
C c;
c.DoThis<LogIt>();
}
// C.h
typedef void (*LogFunction)(const char*, ...);
class C
{
public:
template <LogFunction L>
void DoThis();
template <LogFunction L>
void DoThat();
};
// C.cpp
#include "C.h"
template <LogFunction L>
void C::DoThis()
{
L("DoThis!");
DoThat<L>();
}
template <LogFunction L>
void C::DoThat()
{
L("DoThat!");
}
I'd prefer not to have to resort to having the function passed as a function parameter, i.e.
template <typename F>
void C::DoThis(F f)
{
f("DoThis!");
}
because I'd like to structure the code in such a way that the compiler is able to figure out if the body of LogIt is empty (which it will be for Release builds) and not generate any code for the call, and I'd have to pass it as an argument everywhere in the class.
Can it be done?
Okay I recreated everything,
This error undefined reference to void C::DoThis<&(LogIt(char const*, ...))>() is explained here
Now if you do #include "C.cpp" referring above, this will lead to
undefined reference to void C::DoThat<&(LogIt(char const*, ...))>()
So fix:
template <LogFunction L>
void C::DoThat() //Notice :: used here
{
L("DoThat!");
}
and everything complies and execute !
This is because you have your templates invisible at the point where compiler is supposed to instantiate them as you only have a declaration in C.h and a definition in C.c.
Either move template definitions to header or force instantiation in C.c. You will have to provide LogIt declaration in C.c for that.
You need to put template definition in the same as the place where its declared. So that means you need to put your LogIt function where it was declared in the header file. As of right now, we are not able to explicitly separate template declaration and its definition like that.

Explicit template specialization for const QString& results in unresolved external

#include <QFile>
#include <QString>
// this is some sort of low-level C function
void lowLevelOpenFD(int fd)
{
qDebug("Opened by fd: %d", fd);
}
// as well as this one
void lowLevelOpenName(const char *name)
{
qDebug("Opened by name: %s", name);
}
// this is a wrapper around low-level functions
template<typename FileId>
void callLowLevelOpen(FileId id);
template<>
void callLowLevelOpen(const QString &fileName)
{
lowLevelOpenName(QFile::encodeName(fileName).constData());
}
template<>
void callLowLevelOpen(int fd)
{
lowLevelOpenFD(fd);
}
// this is the function where the most stuff happens
template<typename FileId>
void openInternal(FileId id)
{
// lots of useful stuff goes here
// now we call one of the two low level functions
callLowLevelOpen(id);
// more useful stuff
}
// this is high-level interface to the "open by name" function
void openFile()
{
QString name = "file";
openInternal(name);
}
// this is high-level interface to the "open by FD" function
void openFile(int fd)
{
openInternal(fd);
}
int main()
{
openFile();
openFile(17);
return 0;
}
The problem is that the example above results in
error LNK2019: unresolved external symbol "void __cdecl callLowLevelOpen<class QString>(class QString)" (??$callLowLevelOpen#VQString####YAXVQString###Z) referenced in function "void __cdecl openInternal<class QString>(class QString)" (??$openInternal#VQString####YAXVQString###Z)
As far as I can see it happens because the compiler instantiates openInternal<QString>() when it's called from the first high-level overload. OK, so I thought and modified the code:
// this is high-level interface to the "open by name" function
void openFile()
{
QString name = "file";
openInternal<const QString&>(name);
}
The same problem. And I thought I told the compiler to instantiate the openInternal<const QString&>, so why it is still complains about <class QString>? I also tried this one:
// this is high-level interface to the "open by name" function
void openFile()
{
QString name = "file";
openInternal<const QString&>(static_cast<const QString&>(name));
}
Now this just looks silly and it still doesn't work. I can't explicitly specialize the openInternal() one because it's too large and the very point of this templated mess is to avoid unnecessary code duplication. I can't just rename the low level functions to turn them into overloaded ones, because they are in a third-party C library. The only thing I can do is to replace the first callLowLevelOpen() specialization with
template<>
void callLowLevelOpen(QString fileName)
{
lowLevelOpenName(QFile::encodeName(fileName).constData());
}
Then it works. There is also virtually zero performance penalty so this is a perfectly valid workaround, but I just want to understand what is going on here.
The code above was just an SSCCE, the real code is there if anyone interested. This particular issue is with the gzopen()/gzdopen(), QuaGzipFilePrivate::open() and QuaGzipFile::open() functions.
Since you actually change the signature, I think you actually don't want to specialize your function template but rather use overloading. Here is complete test program using std::string (I somehow prefer depending only on standard classes if the problem can be reproduced there as well):
#include <string>
template <typename T> void f(T);
// #define BROKEN
#if defined(BROKEN)
template <> void f(std::string const&) {}
#else
void f(std::string const&) {}
#endif
int main()
{
std::string s;
f(s);
}
If you #defined BROKEN in this code it won't work.
The reason for this behavior is that the compiler choose the overload based on the primary template. This will never add the const& part. Once this is done the compiler looks for potential specializations of the chosen overload. Since this will never have deduced the notation used for specialization this isn't picked up.
Why then is the f<std::string const&>(s) not picking up the specialization? For me it is, trying with both gcc and clang.

C++ Befriending boost::ptr_map / boost::checked_delete fails

I want to use a boost::ptr_map inside a specific class which stores instances of itself. However, please consider the following example:
#include <boost/checked_delete.hpp>
#include <boost/ptr_container/ptr_map.hpp>
class foo
{
friend void boost::checked_delete<>(foo*);
~foo() {}
};
int main()
{
boost::checked_delete(new foo); // OK
boost::ptr_map<int, foo> foo_map; // error C2248: 'foo::~foo' : cannot access private member declared in class 'foo'
return 0;
}
The error happens at the following line
// verify that types are complete for increased safety
template<class T> inline void checked_delete(T * x)
{
// intentionally complex - simplification causes regressions
typedef char type_must_be_complete[ sizeof(T)? 1: -1 ];
(void) sizeof(type_must_be_complete);
delete x; // error C2248
}
What exactly is going on here? Shouldn't it work? I assume that the problem is that templates are defined in the compilation unit they are included in and boost::checked_delete is called from another compilation unit in the implementation source of bosst::ptr_map. So, it's not the same function I declared as a friend.
However, is there a workaround for this problem?
Try this syntax when declaring the friend:
template <class T>
friend void boost::checked_delete(T*);
Here is the start of the huge error message* from GCC, which is the start of the chain of instantiations (usually, and in this case):
In file included from main.cpp:1:0:
main.cpp: In function 'void boost::checked_delete(T*) [with T = const foo]':
Adding
friend void boost::checked_delete<>(foo const*);
makes the code compile.
(*): 13 lines and 3510 characters for 270 chars/line