This question already has answers here:
Top-level const doesn't influence a function signature
(7 answers)
What is the difference between const int*, const int * const, and int const *?
(23 answers)
Closed 7 months ago.
Messing with some legacy C libraries which use raw pointers I found this to be interesting:
// test.hpp
namespace test
{
void foo(int const* arg);
}
// test.cpp
#include "test.hpp"
void test::foo(int* arg)
{
// implementation...
}
Obviously, this won't compile, because void foo(int*) was not declared anywhere in namespace test.
But what about this?
// test.hpp
namespace test
{
void foo(int* const arg);
}
// test.cpp
#include "test.hpp"
void test::foo(int* arg)
{
arg = nullptr;
}
This successfully compiles with GCC 12.1.0.
But why is that? In the declaration of foo() in test.hpp we say that arg is a constant pointer which can not be modified. Thus, void foo(int* const) and void foo(int*) should technically be the prototypes of two different functions. Why does the compiler consider them to be the same?
Note, this is not a question about the difference between constant pointer and pointer to a constant. The question is about the logic of the compiler - why does it consider two functions to be the same when they are technically not?
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.
According to the example which the Standard provides at N4296::13.3.3 [over.match.best]
namespace A
{
extern "C" void f(int = 5);
}
namespace B
{
extern "C" void f(int = 5);
}
using A::f;
using B::f;
void use()
{
f(3); // OK, default argument was not used for viability
f(); // Error: found default argument twice
}
As the Standard says at N4296::7.5/6 [dcl.link]:
Two declarations for a function with C language linkage with the same
function name (ignoring the namespace names that qualify it) that
appear in different namespace scopes refer to the same function.
I tried to explore such a thing on my own example:
#include <iostream>
namespace A
{
extern "C" void foo(int a = 5){ std::cout << a << "1" << std::endl; }
}
namespace B
{
extern "C" void foo(int a = 5);
}
using A::foo;
using B::foo;
int main()
{
foo(); //Error
foo(2);
}
DEMO
So why does my example work? What's differences between my example and the Standard's example unless I defined the function explicitly in the A namespace? Why is that so important?
As noted already in the comments, there is no relevant difference between the standard's example, and your example. Compilers that properly implement the standard issue a diagnostic for both.
The fact that this is clearly a compiler bug in at least clang and Intel can be seen when you edit the example to the nonsensical
namespace A
{
extern "C" void f(int = 5);
}
namespace B
{
extern "C" void f(int = 3); // different default argument
}
using A::f;
using B::f;
void use()
{
f(); // No error !
}
Despite getting two different default arguments, no error or even a warning is generated. One of the default arguments is used, the first with Intel, the second with clang.
GCC does happen to reject this nonsensical example, so there is no quick and easy way to verify that it is clearly a bug in GCC as well, but that doesn't change the fact that it is: as noted, it silently accepts the example from the standard where the standard points out where an error should be detected.
I am using OpenCV for some image manipulation and it has several functions that must be given a data type to perform correctly. My idea is to template these functions so I do not have to write a separate function for each possible data type it could be.
However, the code I would like the code I am writing to be compatible with some existing C code I have that stores images in memory so I can pass it around easier without writing the image to disc constantly.
The problem I am running into is how to make my .h files and libraries to let the C program call the C++ template functions. I have not had a chance to try it yet, but so far what I have would look something what follows:
foo.h
int foo(int a, int, b);
float foo(float a, float b);
foo.c:
int foo(int a, int b) {
return footemp(a, b);
}
float foo(float a, float b) {
return footemp(a, b);
}
foo.hpp:
template<class t>
t footemp(t a, t b) {
return a + b;
}
Which does not work since it requires my .c file to know about a templated file.
So I am open to suggestions. Thank you in advance for the help
This is possible, with some care. Use a C++ source file foo.cpp:
#include "foo.h"
#include "footemp.hpp"
int foo_int(int a, int b) {
return footemp(a, b);
}
float foo_float(float a, float b) {
return footemp(a, b);
}
In foo.h, use extern "C" to make C-compatible declarations, with an #ifdef to allow use as either C or C++:
#ifdef __cplusplus
extern "C" {
#endif
int foo_int(int a, int, b);
float foo_float(float a, float b);
#ifdef __cplusplus
}
#endif
Compile this as a C++ library—static or dynamic, it doesn’t matter. In your C sources you can now #include "foo.h" and use these functions as you would expect, so long as you link against the library.
I imagine one thing you could do is write the function in C++, declaring the specializations you want, and at the same time defining extern "C" functions that forward the calls to the template functions.
Keep in mind if you're using C for the other stuff, you're going to need to name the functions different; C doesn't do function overloading.
As mday299 mentioned, there is no templates in C.
However, if your C code is contained in a saparate compilation unit (.exe/.dll), you can provide C interface for your template functions almost like you did it:
foo.h
/* C interface */
int fooInt(int a, int, b);
float fooFloat(float a, float b);
foo.hpp
template <class T> foo(T a,T b)
{
return a+b;
}
foo.cpp:
#include "foo.h"
#include "foo.hpp"
int fooInt(int a, int b) {
return foo<int>(a, b);
}
float fooFloat(float a, float b) {
return foo<float>(a, b);
}
Then, project would be compiled separate using c++, and c part would see only foo.h and lib/dll file
No templates in C. That's a C++ feature. Sorry.
There is a good chance that you'll be able to compile your C file with a C++ compiler. So you can build your whole project, both C and C++ sources with a C++ compiler.
You are already compiling it with a C++ compiler, otherwise you wouldn't be able to compile your overloaded foo functions.
I have the following code (stripped down version from actual project to reproduce
the issue) that results in a compiler error on RHEL5 (g++ version 4.1.2):
----------- driver (test.cpp)--------------
#include <iostream>
#include <classa.hpp>
#include <func.hpp>
namespace globals {
static int kth(const A& a) {
return kth(a.ival());
}
}
using namespace globals;
int main() {
A a;
std::cout << func(a) << std::endl;
return 0;
}
----------class A (classa.hpp)------------
class A {
public:
A():val(0){}
const int ival() const {return val;}
private:
int val;
};
------- namespace globals (func.hpp) ------
namespace globals {
int kth(const int& c) {
return c;
}
template <class T>
int func(const T& key) {
return kth(key);
}
}
--------------------------------------------
Compiling it using g++ 4.1.2 gives me the following error:
func.hpp: In function ‘int globals::func(const T&) [with T = A]’:
test.cpp:15: instantiated from here
func.hpp:8: error: invalid initialization of reference of type ‘const int&’ from
expression of type ‘const A’
func.hpp:2: error: in passing argument 1 of ‘int globals::kth(const int&)’
Same code compiles and runs perfectly fine on RHEL4 (g++ version 3.4.6)! Any explanations/ideas/suggestions on how to resolve this error(?) on RHEL5 will
be much appreciated!
Edit:
Thanks Sergey. That is the obvious solution that I am aware of already. But I forgot to add that the restriction is that func.hpp cannot be edited (for e.g., its 3rd party write-protected). Any workarounds?
Here's what happens. When the function func() is defined, the compiler doesn't know about the function kth(const A&) yet because it is defined later in the code. So when it encounters a reference to kth() inside func(), it assumes that it is a reference to kth(const int&). Now when func() is actually instantiated, it fails to compile it because T is A, not int. I am not sure why it works in another version of the compiler, but I think it is because it actually starts resolving references when a template function is instantiated, not when it is declared. But this looks like a bug in the older version because with such behavior a function definition changes depending on where it is instantiated from, which is very confusing.
The only way to fix your code that it works with any compiler would be to put the definition of kth(const A&) between kth(const int&) and func() or a forward declaration of kth(const A&) somewhere above func().
Update
With the restriction of not editing func.hpp the best workaround I can think of is to create a custom header file with something like this:
#include <classa.hpp>
namespace globals {
static int kth(const A& a); // defined later, but used by func.hpp
}
#include <func.hpp>
I also don't see why kth(const A&) is defined as static, but used by a global header. I'd rather put it into the classa.cpp and its declaration into the classa.hpp. But this may be some design feature or artifact I am not aware of.