実用的な#include ="でかいデータ用ファイル"について
背景・動機
研究なのでデータ処理のためのCコードを書いているとき,アプリオリに定義されているデータ構造をファイルIOを用いず使いたい場合がある.
私の場合だと標準地域メッシュを列にとる時系列データベースを組んでおり,それに関連したコードを書いている.
この場合だと列名は固定で,また勝手に列名を変えられたくもないので,ハードコードしてしまいたい欲求がある.
また今回であれば私以外のメンバーも用いるプログラムなため,可能であればポータブル(単一バイナリで実行可能)にしたい.
愚直な実装
みんな大好きC言語を用いているため,普通に考えれば以下のような実装ができる.
//
// Created by ryuzot on 25/01/02.
//
#ifndef MESHID_OPS_H
#define MESHID_OPS_H
#include <stdint.h>
static const uint32_t meshid_list[] = {
#include "../external/meshids/meshid_1_2rd.csv"
};
#endif //MESHID_OPS_H
データの内容はどうでも良いが,これは../external/meshids/meshid_1_2rd.csvに
存在するcsvファイル(csvとはいえ改行は無くカンマのみ)を#include
ディレクティブで展開し,uint32の配列として用いている.
他にも,バイナリーデータとして用いたいものについてはxxd
コマンドを用いCヘッダーファイルを生成する手法も主流である.
[hoge]$ xxd -i meshid_mobaku.mph > meshid_mobaku.h
[hoge]$ head meshid_mobaku.h
unsigned char meshid_mobaku_mph[] = {
0x63, 0x68, 0x6d, 0x00, 0xb3, 0xb3, 0x17, 0x00, 0x02, 0x00, 0x00, 0x00,
0x0c, 0x00, 0x00, 0x00, 0x6a, 0x65, 0x6e, 0x6b, 0x69, 0x6e, 0x73, 0x00,
0x6d, 0xc2, 0x22, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x6a, 0x65, 0x6e, 0x6b,
0x69, 0x6e, 0x73, 0x00, 0x4e, 0xc6, 0x23, 0x00, 0x7e, 0x89, 0x31, 0x00,
0xb3, 0xb3, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
ところで,私は一昔のハッカーとは違い,C言語でも強力なIDEを用いコードを書いている.
vim縛りをしている一部のヤバい人とは違うのである.
私の最も愛用しているIDEはJetbrainsのもの(C言語用であればClion)だが,これらはコーディング支援のために#include
するものはすべて読み込む習性がある.
これは多分主流流行のvscodeとかでも同様.
上記手法だと,#include
するファイルが数MB以上になる場合,IDEがめちゃんこ重くなる.
まあこれは設定次第でどうとでもなるかもだが,そもそもコンパイル時間も多少長くなってよろしくない.
上記ファイルはスタテックなため,リンク時にうまく処理できるはずである.
事前オブジェクトファイル化とcmakeでの利用
まずstaticを外し,また配列サイズを返す変数を入れる.
#include <stdint.h>
#include <stddef.h>
const uint32_t meshid_list[] = {
#include "meshid_1_2rd.csv"
};
const size_t meshid_list_size = sizeof(meshid_list)/sizeof(meshid_list[0]);
コンパイラでオブジェクトファイル作成
[hoge]$ clang -c meshid.c -o meshid.o
次に,利用側から見れるようにするため,以下のようなヘッダファイルを作成
//
// Created by ryuzot on 25/01/02.
//
#ifndef MESHID_OPS_H
#define MESHID_OPS_H
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
extern const size_t meshid_list_size;
extern const uint32_t meshid_list[];
#ifdef __cplusplus
}
#endif
#endif //MESHID_OPS_H
このヘッダはプロジェクトの中に作ること.
cmakelistでは,まず普通にadd_executableに含めたあと,SET_SOURCE_FILES_PROPERTIES
で外部からのオブジェクトファイルであることを明示させる.
...前略...
SET(OBJS
${CMAKE_CURRENT_SOURCE_DIR}/external/meshids/meshid.o
)
add_library(lib
いろいろ
${OBJS}
)
...中略...
add_executable(test_meshid_ops
tests/test_meshid_ops.c
)
target_include_directories(test_meshid_ops PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include
)
target_link_libraries(test_meshid_ops PUBLIC
lib
)
SET_SOURCE_FILES_PROPERTIES(
${OBJS}
PROPERTIES
EXTERNAL_OBJECT true
GENERATED true
)
感想
cmake周りで思いのほか時間食った
参考
Discussion