CMake: ターゲットプロパティ "VISIBILITY_INLINES_HIDDEN" を使ってインライン関数のシンボルを隠す方法


VISIBILITY_INLINES_HIDDEN は、CMake のターゲットプロパティであり、インライン関数のシンボルを隠すためのコンパイラフラグ (-fvisibility-inlines-hidden など) を使用するか否かを決定します。これは、ライブラリやエクスポートを持つ実行ファイルにのみ影響を与えます。

影響

このプロパティが ON に設定されている場合、CMake はコンパイル時に -fvisibility-inlines-hidden フラグを指定します。これにより、インライン関数のシンボルがリンカから隠され、他のモジュールからのアクセスが制限されます。これは、コードのサイズを小さくし、リンカエラーを回避するのに役立ちます。

設定方法

以下の例のように、set_target_properties コマンドを使用して VISIBILITY_INLINES_HIDDEN プロパティを設定できます。

add_library(MY_LIB ${MY_SOURCES})
set_target_properties(MY_LIB PROPERTIES C_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN ON)

上記の例では、MY_LIB ターゲットに対して VISIBILITY_INLINES_HIDDEN プロパティが ON に設定されています。

  • このプロパティは、GCC、Clang、Visual Studioなどの多くのコンパイラでサポートされています。
  • VISIBILITY_INLINES_HIDDEN プロパティは、CMake 3.0 以降でのみ使用できます。

以下の例は、VISIBILITY_INLINES_HIDDEN プロパティを使用してインライン関数のシンボルを隠す方法を示しています。

cmake_minimum_required(VERSION 3.0)

project(MY_PROJECT)

add_library(MY_LIB ${MY_SOURCES})
set_target_properties(MY_LIB PROPERTIES C_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN ON)

add_executable(MY_APP ${MY_APP_SOURCES})
target_link_libraries(MY_APP MY_LIB)

上記の例では、MY_LIB ライブラリのインライン関数のシンボルは隠され、MY_APP アプリケーションからはアクセスできません。



ライブラリとアプリケーションの例

この例では、mylib.c というソースファイルでインライン関数を定義し、mylib.so という共有ライブラリと myapp という実行ファイルを作成します。ライブラリのインライン関数のシンボルは隠され、実行ファイルからはアクセスできません。

ソースファイル

// mylib.c
#include <stdio.h>

inline void my_inline_function() {
  printf("This is an inline function.\n");
}

void non_inline_function() {
  printf("This is not an inline function.\n");
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.0)

project(MY_PROJECT)

add_library(MY_LIB mylib.c)
set_target_properties(MY_LIB PROPERTIES C_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN ON)

add_executable(MY_APP myapp.c)
target_link_libraries(MY_APP MY_LIB)

myapp.c

#include <stdio.h>
#include "mylib.h"

int main() {
  non_inline_function(); // This is OK
  // my_inline_function(); // This will cause a linker error

  return 0;
}

このコードを実行すると、以下の出力が得られます。

This is not an inline function.

my_inline_function はインライン関数なので、実行ファイルからは直接呼び出すことができません。

この例では、mylib1.cmylib2.c というソースファイルでインライン関数を定義し、それぞれ mylib1.somylib2.so という共有ライブラリを作成します。mylib1.so のインライン関数のシンボルは隠され、mylib2.so のインライン関数のシンボルは隠されません。

ソースファイル

// mylib1.c
#include <stdio.h>

inline void mylib1_inline_function() {
  printf("This is an inline function in mylib1.\n");
}

void non_inline_function() {
  printf("This is not an inline function.\n");
}

// mylib2.c
#include <stdio.h>

inline void mylib2_inline_function() {
  printf("This is an inline function in mylib2.\n");
}

void non_inline_function() {
  printf("This is not an inline function.\n");
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.0)

project(MY_PROJECT)

add_library(MY_LIB1 mylib1.c)
set_target_properties(MY_LIB1 PROPERTIES C_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN ON)

add_library(MY_LIB2 mylib2.c)

add_executable(MY_APP myapp.c)
target_link_libraries(MY_APP MY_LIB1 MY_LIB2)

myapp.c

#include <stdio.h>
#include "mylib1.h"
#include "mylib2.h"

int main() {
  non_inline_function(); // This is OK
  mylib1_inline_function(); // This will cause a linker error
  mylib2_inline_function(); // This is OK

  return 0;
}
This is not an inline function.
This is an inline function in mylib2.

mylib1.so のインライン関数は隠されているため、myapp からは直接呼び出すことができません。一方、mylib2.so のインライン関数は隠されていないため、myapp から直接呼び出すことができます。



static キーワード

インライン関数を static キーワードで宣言することで、その関数のシンボルをファイルスコープに限定することができます。これは、インライン関数へのアクセスをそのファイル内にのみ制限するため、最も単純で効果的な方法です。

利点

  • コンパイル時にシンボルを隠すため、オーバーヘッドが少ない
  • シンプルで理解しやすい

欠点

  • インライン関数が他のファイルから呼び出される可能性がある場合、意図せずシンボルが公開されてしまう可能性がある
  • ライブラリや共有オブジェクトでは使用できない


// mylib.c
static inline void my_inline_function() {
  printf("This is an inline function.\n");
}

void non_inline_function() {
  printf("This is not an inline function.\n");
}

匿名名前空間

利点

  • ライブラリや共有オブジェクトで使用できる

欠点

  • コンパイル時にシンボルを隠すため、オーバーヘッドが少ない
  • static キーワードよりも複雑で理解しにくい


// mylib.c
namespace {
inline void my_inline_function() {
  printf("This is an inline function.\n");
}
}

void non_inline_function() {
  printf("This is not an inline function.\n");
}

マクロ

マクロを使用して、インライン関数を呼び出すためのラッパーを作成することができます。この方法は、インライン関数のシンボルを完全に隠すことができ、コンパイラによる最適化を抑制することができます。

利点

  • コンパイラによる最適化を抑制できる
  • インライン関数のシンボルを完全に隠すことができる

欠点

  • マクロのオーバーヘッドが発生する
  • 複雑で理解しにくい


// mylib.c
#define MY_INLINE_FUNCTION() \
  inline void { \
    printf("This is an inline function.\n"); \
  }

void non_inline_function() {
  printf("This is not an inline function.\n");
}

int main() {
  MY_INLINE_FUNCTION();
  return 0;
}

コンパイラ固有のオプション

多くのコンパイラは、インライン関数のシンボルを隠すためのオプションを提供しています。これらのオプションは、CMake の VISIBILITY_INLINES_HIDDEN プロパティよりも詳細な制御を提供することができます。

利点

  • 詳細な制御を提供できる

欠点

  • CMake との互換性がない場合がある
  • コンパイラごとに異なるオプションがある


  • Visual Studio: /visibility:hidden
  • Clang: -fvisibility=hidden
  • GCC: -fvisibility=hidden

これらの代替方法のいずれを選択するかは、それぞれの状況や要件によって異なります。単純で効果的な方法が必要な場合は、static キーワードが最適です。より柔軟性が必要な場合は、匿名名前空間を使用することができます。インライン関数のシンボルを完全に隠す必要がある場合は、マクロを使用することができます。コンパイラ固有のオプションを使用すると、より詳細な制御が可能になりますが、CMake との互換性がない場合があります。