Thread overview
Passing pointer to extern(C++) templated function
Oct 13, 2020
Jamie
Oct 13, 2020
James Blachly
Oct 13, 2020
Ali Çehreli
Oct 14, 2020
Jamie
Oct 13, 2020
kinke
Oct 14, 2020
Jamie
Oct 14, 2020
kinke
October 13, 2020
I'm having difficulties linking templated functions with multiple pointer arguments with extern(C++).

a.cpp
-----
template<typename T> void func1(T *b){}
template void func1<int>(int *b);

template<typename T> void func2(const T *a){}
template void func2<int>(const int *a);

template<typename T> void func3(T *b, const T *a){}
template void func3<int>(int *b, const int *a);

template<typename T> void func4(const T *a, T *b){}
template void func4<int>(const int *a, int *b);

void func5(int *b, const int *a){}
void func6(const int *a, int *b){};
-----

main.d
-----
extern (C++)
{
    void func1(T)(T *b);
    void func2(T)(const T *a);
    void func3(T)(T *b, const T *a);
    void func4(T)(const T *a, T *b);
    void func5(int *b, const int *a);
    void func6(const int *a, int *b);
}

void main()
{
    int i = 4;
    int *pi = &i;

    func1(pi);
    func2(&i);
    func3(pi, &i);
    func4(&i, pi);
    func5(pi, &i);
    func6(&i, pi);
}
-----

Building with:
g++ -c a.cpp
dmd main.d a.o

Throws the error:
/usr/bin/ld: main.o: in function `_Dmain':
main.d:(.text._Dmain[_Dmain]+0x31): undefined reference to `void func3<int>(int*, int*)'
/usr/bin/ld: main.d:(.text._Dmain[_Dmain]+0x3e): undefined reference to `void func4<int>(int const*, int const)'
collect2: error: ld returned 1 exit status
Error: linker exited with status 1

When I inspect the object files, I see the following:
a.o
-----
void func1<int>(int*)
void func2<int>(int const*)
void func3<int>(int*, int const*)
void func4<int>(int const*, int*)
func5(int*, int const*)
func6(int const*, int*)
-----

main.o
-----
void func1<int>(int*)
void func2<int>(int const*)
void func3<int>(int*, int*)
void func4<int>(int const*, int const)
func5(int*, int const*)
func6(int const*, int*)
-----

It appears that func3 and func4 take on different types depending on other variables being present? Is this expected? Why do they differ from the non-templated functions func5 and func6 respectively? How should I achieve this instead?

October 13, 2020
On 10/13/20 5:23 AM, Jamie wrote:
> Building with:
> g++ -c a.cpp
> dmd main.d a.o
> 
> Throws the error:
> /usr/bin/ld: main.o: in function `_Dmain':
> main.d:(.text._Dmain[_Dmain]+0x31): undefined reference to `void func3<int>(int*, int*)'
> /usr/bin/ld: main.d:(.text._Dmain[_Dmain]+0x3e): undefined reference to `void func4<int>(int const*, int const)'
> collect2: error: ld returned 1 exit status
> Error: linker exited with status 1
> 

Is the template even instantiated? With just a template definition and an object file I am surprised you even have a symbol?
October 13, 2020
On 10/13/20 4:11 PM, James Blachly wrote:
> On 10/13/20 5:23 AM, Jamie wrote:
>> Building with:
>> g++ -c a.cpp
>> dmd main.d a.o
>>
>> Throws the error:
>> /usr/bin/ld: main.o: in function `_Dmain':
>> main.d:(.text._Dmain[_Dmain]+0x31): undefined reference to `void func3<int>(int*, int*)'
>> /usr/bin/ld: main.d:(.text._Dmain[_Dmain]+0x3e): undefined reference to `void func4<int>(int const*, int const)'
>> collect2: error: ld returned 1 exit status
>> Error: linker exited with status 1
>>
> 
> Is the template even instantiated? With just a template definition and an object file I am surprised you even have a symbol?

Yes, they all have explicit template instantiations on the C++ side.

I think the issue is with D's "turtles all the way down" style const. My workaround would be to define wrapper functions that may need to do casting on one or the other side.

Ali

October 13, 2020
On Tuesday, 13 October 2020 at 09:23:48 UTC, Jamie wrote:
> It appears that func3 and func4 take on different types depending on other variables being present? Is this expected?

Nope, it's a bug in the Itanium C++ mangler, please file a bug. MSVC++ mangling seems fine, after fixing the D declarations to

void func3(T)(T* b, const(T)* a);
void func4(T)(const(T)* a, T* b);

[A D `const T*` is equivalent to C++ `const T* const`; this matters for MSVC mangling...].
October 14, 2020
On Tuesday, 13 October 2020 at 23:47:24 UTC, kinke wrote:
> On Tuesday, 13 October 2020 at 09:23:48 UTC, Jamie wrote:
>> It appears that func3 and func4 take on different types depending on other variables being present? Is this expected?
>
> Nope, it's a bug in the Itanium C++ mangler, please file a bug. MSVC++ mangling seems fine, after fixing the D declarations to
>
> void func3(T)(T* b, const(T)* a);
> void func4(T)(const(T)* a, T* b);
>
> [A D `const T*` is equivalent to C++ `const T* const`; this matters for MSVC mangling...].

Happy to file a bug, but if it was a bug in the mangler wouldn't both C++ and D get the same result? Assuming D uses the same mangler for the extern(C++) stuff.
October 14, 2020
On Tuesday, 13 October 2020 at 23:39:38 UTC, Ali Çehreli wrote:
> On 10/13/20 4:11 PM, James Blachly wrote:
>> On 10/13/20 5:23 AM, Jamie wrote:
>
> I think the issue is with D's "turtles all the way down" style const. My workaround would be to define wrapper functions that may need to do casting on one or the other side.
>
> Ali

Why would the 'turtles all the way down' change function arguments? They are on the same 'level' whereas I took the transitive const to be applied through different depths in functions / classes etc.

Thanks for the suggestion, my current work around is to just remove the const but casting sounds a bit safer.
October 14, 2020
On Wednesday, 14 October 2020 at 00:25:56 UTC, Jamie wrote:
> Happy to file a bug, but if it was a bug in the mangler wouldn't both C++ and D get the same result? Assuming D uses the same mangler for the extern(C++) stuff.

Bug in the D frontend implementation of Itanium C++ mangling. https://github.com/dlang/dmd/blob/master/src/dmd/cppmangle.d