※初歩的な勘違いをしていたのでメモ
次のコードは動かない
といってもリンクができないだけで分割コンパイルはできる(できてしまう)
#pragma once namespace Hoge{ typedef float hoge; hoge setHoge(); void getHoge(hoge value); };
#include <iostream> #include "hoge.hpp" using namespace std; using namespace Hoge; hoge setHoge(){ return ((hoge)10); } void getHoge(hoge value){ cout << value << endl; }
#include <iostream> #include "hoge.hpp" using namespace Hoge; int main(){ hoge x = setHoge(); // x = 10 getHoge(x); // print 10 return 0; }
make: clang++ -std=c++20 -c hoge.cpp main.cpp nm hoge.o > hoge.sym nm main.o > main.sym clang++ hoge.o main.o -o test
clang++ -std=c++20 -c hoge.cpp main.cpp nm hoge.o > hoge.sym nm main.o > main.sym clang++ hoge.o main.o -o test Undefined symbols for architecture x86_64: "Hoge::getHoge(float)", referenced from: _main in main.o "Hoge::setHoge()", referenced from: _main in main.o ld: symbol(s) not found for architecture x86_64 clang: error: linker command failed with exit code 1 (use -v to see invocation) make: *** [make] Error 1
なぜか?
上のスクリプトで分割コンパイルすると,それぞれ#include "hoge.hpp"
が展開された上で翻訳単位としてhoge
とmain
の2つが発生して,それぞれが処理されてシンボルテーブルを持つオブジェクトとして出てくる
main
側はHoge
周りの実装をしていないため,hoge.hpp
の宣言した内容に期待して,名前空間Hoge
を含めたシンボルをあらかじめ設定する
ゆえにhoge
側は名前空間Hoge
を含めたシンボルを実体に持つような実装が期待されており,そうでなければリンク時に名前を解決できず失敗する
ソース側でusing namespace Hoge
を利用して(名前空間の解決の記法を省略して)実装することは不可能であり,それはコンパイラが名前空間の探索と適切な配置よりも先に勝手に大域的な配置をしてしまうためである…
なんでそうなのかは知りませんが,歴史的な経緯とかあるのでしょうか?やっぱり外部リンケージがデフォルトな言語だからなんですかね,extern
とconst
も難解すぎてこのままではstatic
おじさんになってしまう…
hoge
側
nm hoge.o | grep Hoge
0000000000000010 T __Z7getHogef 0000000000000000 T __Z7setHogev
main
側
nm main.o | grep Hoge
U __ZN4Hoge7getHogeEf U __ZN4Hoge7setHogeEv
解決策
namespace Hoge{}
の中に実装を書く
多分これが一番楽だと思います,ただしクソデカネストが許容できる場合の話,俺は辛い耐えられない
#include <iostream> #include "hoge.hpp" using namespace std; using namespace Hoge; namespace Hoge{ hoge setHoge(){ return ((hoge)10); } void getHoge(hoge value){ cout << value << endl; } }
実装の名前に必ずHoge::
をつける
using namespace Hoge;
しつつクソデカネストを使わないなら,多分これが一番楽だと思います
また宣言されたtypedef
やclass
の名前スコープを無視できる,人によっては折衷案に見える?
#include <iostream> #include "hoge.hpp" using namespace std; using namespace Hoge; hoge Hoge::setHoge(){ return ((hoge)10); } void Hoge::getHoge(hoge value){ cout << value << endl; }