D言語 → C++ → C# 間でテンプレートを使用する

# はじめに
`C++`（`C++/CLI`）で定義したテンプレートを`D言語`で利用可能です。
また、`C#`で定義したテンプレート（ジェネリック）を`C++`（`C++/CLI`）で利用可能です。
この記事は、テンプレートの利用例について書きました。

# 環境
以下の環境でコンパイルしています。

* Windows 10
* [Visual C++ ビルドツール 2019](https://visualstudio.microsoft.com/ja/downloads/)　→　[インストール手順](https://www.kkaneko.jp/tools/win/buildtool.html)
* [DMD](https://dlang.org/download.html) v2.095.0

# ソースコード
テンプレートの利用例として、`D言語`から基本データ型を引数にして、`C++`の関数を呼び出す処理を作成しました。
`callWrapper`は、`C++`側で定義したテンプレート関数です。`D言語`側で使用するために関数宣言しています。
`printType`は、`D`側で実装、使用するテンプレート関数です。

```D:dfunc.d
extern (C):
int printf(immutable(char)* format, ...);

void main()
{
	printf("|D|C++|C#|\n");
	printf("|:--|:--|:--|\n");
	printType(cast(byte)0);
	printType(cast(short)0);
	printType(cast(int)0);
	printType(cast(long)0);
	printType(cast(ubyte)0);
	printType(cast(ushort)0);
	printType(cast(uint)0);
	printType(cast(ulong)0);
	printType(cast(float)0);
	printType(cast(double)0);
	printType(cast(char)0);
	printType(cast(wchar)0);
	printType(cast(dchar)0);
}

extern (C++):
void callWrapper(T)(T t);

void printType(T)(T t)
{
	printf("|%s|".ptr, T.stringof.ptr);
	callWrapper(t);
}
```

`C++`のソースコードの中で`C#`で作成したテンプレート関数`CSharpClass::printType`を呼び出しています。
「テンプレートの実体化」については、後ほど補足します。

```C++:cppc.cpp
#using <csc.netmodule>
using namespace CSharpLibrary;
#include <stdio.h>
#include <typeinfo>

template<class T>
void callWrapper(T t)
{
	printf("%s|", typeid(T).name());
	CSharpClass::printType(t);
}

// 関数のテンプレートの実体化
void dummy(void)
{
	callWrapper((__int8)0);
	callWrapper((__int16)0);
	callWrapper((__int32)0);
	callWrapper((__int64)0);
	callWrapper((signed char)0);
	callWrapper((unsigned char)0);
	callWrapper((unsigned short)0);
	callWrapper((unsigned int)0);
	callWrapper((unsigned __int64)0);
	callWrapper((float)0);
	callWrapper((double)0);
	callWrapper((char16_t)0);
	callWrapper((char32_t)0);
}
```
ソースコード`csc.cs`の`printType`は、自身の`Type`を出力するテンプレート関数です。
これを使って、`D言語`からの呼び出し時の基本データ型が`C#`でどのように扱われるかを確認したいと思います。

```C#:csc.cs
using System;

namespace CSharpLibrary
{
	public class CSharpClass
	{
		public static void printType<T>(T t)
		{
			Console.WriteLine(t.GetType().FullName + "|");
		}
	}
}
```
# テンプレートの実体化（テンプレートのインスタンス化）
`C++`側でインスタンス化されないテンプレート定義はオブジェクトファイルに格納されないため、D言語とのオブジェクトファイルとのリンク時にエラーが発生します。
このため、[明示的なインスタンス化](https://docs.microsoft.com/ja-jp/cpp/cpp/explicit-instantiation?view=msvc-160)が必要となります。

D言語の[レファレンス（Interfacing to C++）](https://dlang.org/spec/cpp_interface.html#cpp-templates)でも`C++`側でインスタンス化を行っていたので、それに倣いました。

```d:レファレンスから抜粋
template<class T>
void printThreeNext(Foo<T> foo)
{
    for(size_t i = 0; i < 3; ++i)
    {
        std::cout << foo.get() << std::endl;
        increment(foo);
    }
}

// The following two functions ensure that the required instantiations of
// printThreeNext are provided by this code module
void printThreeNexti(Foo<int> foo)
{
    printThreeNext(foo);
}

void printThreeNextc(Foo<char> foo)
{
    printThreeNext(foo);
}
```

## 参考：テンプレートの実体化を行っていない（`C++`に`dummy関数を追加しない`）場合に出力されるエラー
「"printf" は未解決」エラーが発生する場合、[この記事](https://docs.microsoft.com/ja-jp/cpp/porting/visual-cpp-change-history-2003-2015?view=msvc-160#stdioh-and-conioh)にある通り、`legacy_stdio_definitions.lib`をリンクする必要があります。

```text:エラー例
D:\Dev> cl /clr cppc.cpp dfunc.obj csc.netmodule /link /NODEFAULTLIB:libcmt.lib /LTCG
Microsoft(R) C/C++ Optimizing Compiler Version 19.24.28314
Microsoft (R) .NET Framework の場合 バージョン 4.08.4250.0
Copyright (C) Microsoft Corporation.  All rights reserved.

cppc.cpp
Microsoft (R) Incremental Linker Version 14.24.28314.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:cppc.exe
/NODEFAULTLIB:libcmt.lib
/LTCG
cppc.obj
dfunc.obj
csc.netmodule
dfunc.obj : error LNK2001: 外部シンボル "printf" は未解決です。
dfunc.obj : error LNK2001: 外部シンボル ""void __cdecl callWrapper<signed char>(signed char)" (??$callWrapper@C@@YAXC@Z)" は未解決です。
dfunc.obj : error LNK2001: 外部シンボル ""void __cdecl callWrapper<short>(short)" (??$callWrapper@F@@YAXF@Z)" は未解決です。
dfunc.obj : error LNK2001: 外部シンボル ""void __cdecl callWrapper<int>(int)" (??$callWrapper@H@@YAXH@Z)" は未解決です。
dfunc.obj : error LNK2001: 外部シンボル ""void __cdecl callWrapper<__int64>(__int64)" (??$callWrapper@_J@@YAX_J@Z)" は未解決です。
dfunc.obj : error LNK2001: 外部シンボル ""void __cdecl callWrapper<unsigned char>(unsigned char)" (??$callWrapper@E@@YAXE@Z)" は未解決です。
dfunc.obj : error LNK2001: 外部シンボル ""void __cdecl callWrapper<unsigned short>(unsigned short)" (??$callWrapper@G@@YAXG@Z)" は未解決です。
dfunc.obj : error LNK2001: 外部シンボル ""void __cdecl callWrapper<unsigned int>(unsigned int)" (??$callWrapper@I@@YAXI@Z)" は未解決です。
dfunc.obj : error LNK2001: 外部シンボル ""void __cdecl callWrapper<unsigned __int64>(unsigned __int64)" (??$callWrapper@_K@@YAX_K@Z)" は未解決です。
dfunc.obj : error LNK2001: 外部シンボル ""void __cdecl callWrapper<float>(float)" (??$callWrapper@M@@YAXM@Z)" は未解決です。
dfunc.obj : error LNK2001: 外部シンボル ""void __cdecl callWrapper<double>(double)" (??$callWrapper@N@@YAXN@Z)" は未解決です。
dfunc.obj : error LNK2001: 外部シンボル ""void __cdecl callWrapper<char>(char)" (??$callWrapper@D@@YAXD@Z)" は未解決です。
dfunc.obj : error LNK2001: 外部シンボル ""void __cdecl callWrapper<char16_t>(char16_t)" (??$callWrapper@_S@@YAX_S@Z)" は未解決です。
dfunc.obj : error LNK2001: 外部シンボル ""void __cdecl callWrapper<char32_t>(char32_t)" (??$callWrapper@_U@@YAX_U@Z)" は未解決です。
cppc.exe : fatal error LNK1120: 14 件の未解決の外部参照
```

# コンパイル
コンパイルは「VS2019用 x64 Native Tools コマンドプロンプト」から実行します。
`D`、`C++`、`C#`を１つのEXEファイル`cppc.exe`にまとめました。

```text:コマンドプロンプト
D:\Dev> csc.exe /target:module csc.cs
Microsoft (R) Visual C# Compiler バージョン 3.4.1-beta4-19610-02 (c4e5d138)
Copyright (C) Microsoft Corporation. All rights reserved.

D:\Dev> dmd -m64 -c dfunc.d

D:\Dev> cl /clr cppc.cpp dfunc.obj csc.netmodule /link /NODEFAULTLIB:libcmt.lib /LTCG
Microsoft(R) C/C++ Optimizing Compiler Version 19.24.28314
Microsoft (R) .NET Framework の場合 バージョン 4.08.4250.0
Copyright (C) Microsoft Corporation.  All rights reserved.

cppc.cpp
Microsoft (R) Incremental Linker Version 14.24.28314.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:cppc.exe
/NODEFAULTLIB:libcmt.lib
/LTCG
cppc.obj
dfunc.obj
csc.netmodule
コード生成しています。
コード生成が終了しました。

D:\Dev> dir /b
cppc.cpp
cppc.exe
cppc.obj
csc.cs
csc.netmodule
dfunc.d
dfunc.obj
```

# 実行結果
実行結果は、以下のような`Markdown`形式の表となります。
`D`、`C++`、`C#`の基本データ型についての比較表になります。

|D|C++|C#|
|:--|:--|:--|
|byte|signed char|System.SByte|
|short|short|System.Int16|
|int|int|System.Int32|
|long|__int64|System.Int64|
|ubyte|unsigned char|System.Byte|
|ushort|unsigned short|System.UInt16|
|uint|unsigned int|System.UInt32|
|ulong|unsigned __int64|System.UInt64|
|float|float|System.Single|
|double|double|System.Double|
|char|char|System.SByte|
|wchar|char16_t|System.Char|
|dchar|char32_t|System.UInt32|