先頭へ戻る

マクロとその代替 解答ページ | Programming Place Plus C++編【言語解説】 第10章

Programming Place Plus トップページC++編

先頭へ戻る

C++編で扱っている C++ は 2003年に登場した C++03 という、とても古いバージョンのものです。C++ はその後、C++11 -> C++14 -> C++17 -> C++20 と更新されており、今後も 3年ごとに更新されます。
なかでも C++11 での更新は非常に大きなものであり、これから C++ の学習を始めるのなら、C++11 よりも古いバージョンを対象にするべきではありません。特に事情がないなら、新しい C++ を学んでください。 当サイトでは、C++14 をベースにした新C++編を作成中です。

問題①

問題① extern “C” { } で囲むコードは、ヘッダファイルの数が多くなると毎回記述しなければならず、わりと不便です。#define を利用して、記述量を減らせないでしょうか?


次のようなマクロを用意しておけば、記述量を減らすことが可能です。

#ifdef __cplusplus
#define EXTERN_C_BEGIN  extern "C" {
#define EXTERN_C_END    }
#else
#define EXTERN_C_BEGIN
#define EXTERN_C_END
#endif

EXTERN_C_BEGIN と EXTERN_C_END は、__cplusplus が有効になっているときにだけ、意味のある文字の並びに置換され、__cplusplus が無効であれば、空になります。この2つを使えば、

EXTERN_C_BEGIN
int function(int num);
EXTERN_C_END

こんな感じで書けます。

用意したマクロ自体は、どこかのヘッダファイルに入れておいて、#include で取り込むことになります。extern “C” を書きたい箇所がそれなりに多くないと効果は薄いかもしれませんが、数があれば効果的です。

問題②

問題② 次のヘッダファイルを、C言語と C++ で共用できるように修正してください。

/* sub.h */
#ifndef SUB_H_INCLUDED
#define SUB_H_INCLUDED

namespace MyLib {

enum Type {
    TYPE_A,
    TYPE_B,
};

struct Data {
    Type  type;
    char  name[16];
};


void print_data(const Data* data);

}

#endif

当たり前ですが、C言語で使えない機能を使ってしまうとC言語からは使えません。そこでまず、名前空間を排除しましょう。単純に消してしまうだけでも一応大丈夫ですが、名前空間の意図を考えると、関数や構造体などの名前のプリフィックスを付加することで置き換えた方が良いでしょう。

/* sub.h */
#ifndef SUB_H_INCLUDED
#define SUB_H_INCLUDED

enum MyLib_Type {
    MYLIB_TYPE_A,
    MYLIB_TYPE_B,
};

struct MyLib_Data {
    MyLib_Type  type;
    char  name[16];
};


void MyLib_print_data(const MyLib_Data* data);

#endif

次に、MyLib_print_data関数の仮引数ですが、C言語の場合、「const struct MyLib_Data* data」というように、structキーワードを付けないといけません(第2章参照)。あるいは、構造体定義の際に typedef を使って、MyLib_Data を別名として定義する必要があります(C言語編第26章参照)。ここでは後者の方法で修正してみます。

列挙型の方も同じ事情がある(C言語編第50章参照)ので、こちらも修正します。

/* sub.h */
#ifndef SUB_H_INCLUDED
#define SUB_H_INCLUDED

typedef enum MyLib_Type_tag {
    MYLIB_TYPE_A,
    MYLIB_TYPE_B,
} MyLib_Type;

typedef struct MyLib_Data_tag {
    MyLib_Type  type;
    char  name[16];
} MyLib_Data;


void MyLib_print_data(const MyLib_Data* data);

#endif

あとは、extern “C” を付ければ完了です。

/* sub.h */
#ifndef SUB_H_INCLUDED
#define SUB_H_INCLUDED

typedef enum MyLib_Type_tag {
    MYLIB_TYPE_A,
    MYLIB_TYPE_B,
} MyLib_Type;

typedef struct MyLib_Data_tag {
    MyLib_Type  type;
    char  name[16];
} MyLib_Data;


#ifdef __cplusplus
extern "C" {
#endif

void MyLib_print_data(const MyLib_Data* data);

#ifdef __cplusplus
}
#endif

#endif

問題③

問題③ 関数テンプレートをC言語からも利用することは可能でしょうか?


C++ に固有の機能を、C言語から使うことはできませんから、関数テンプレートを直接的に利用することは不可能です。

ただ、どんな型でも使えるという部分をある程度諦めることで、間接的に関数テンプレートを利用できます。そのためには、C言語から使える形の関数を用意して、その中から関数テンプレートを呼び出すという手段を取ります。

たとえば、次のようなヘッダファイルがあるとします。

// write.h

#ifndef WRITE_H_INCLUDED
#define WRITE_H_INCLUDED

#include <iostream>

template <typename T>
void write(T a);


template <typename T>
void write(T a)
{
    std::cout << a << std::endl;
}

#endif

このヘッダファイルで定義されている write関数テンプレートを使いたいとします。そこで、まず write_c.h のようなC言語インターフェース用のヘッダファイルを追加します。

// write_c.h

#ifndef WRITE_C_H_INCLUDED
#define WRITE_C_H_INCLUDED

#ifdef __cplusplus
extern "C" {
#endif

void write_int(int a);
void write_string(const char* a);

#ifdef __cplusplus
}
#endif

#endif

write_int と write_string という2つの関数を宣言しており、それぞれ、int型版の write関数、const char*版の write関数です。この中身を記述します。

// write_c.cpp
#include "write_c.h"
#include "write.h"

void write_int(int a)
{
    write(a);
}

void write_string(const char* a)
{
    write(a);
}

このように、関数テンプレート版の write関数を呼び出します。write_c.h があれば、C言語のプログラムからでも、int型と const char*型だけに限って、write関数を使うことができるようになりました。

/* main.c */
#include "write_c.h"

int main(void)
{
    write_int(100);
    write_string("abc");

    return 0;
}

実行結果:

100
abc

問題④

問題④ 次の関数形式マクロを、より安全な方法で置き換えてください。

#define MAX(a,b) ((a) > (b) ? (a) : (b))


インライン関数と関数テンプレートを使います。

#include <iostream>

template <typename T>
inline T max(T a, T b)
{
    return (a > b ? a : b);
}

int main()
{
    int a = 10;
    int ans1 = max(a, 5);
    int ans2 = max(++a, 5);

    std::cout << ans1 << "\n"
              << ans2 << std::endl;
}

実行結果:

10
11

関数形式マクロだと、ans2 には 12 という誤った結果が代入されますが、関数テンプレートなら、正しく 11 が代入されます。


参考リンク


更新履歴

’2018/8/21 章の内容を全面的に変更して新規作成。



第10章のメインページへ

C++編のトップページへ

Programming Place Plus のトップページへ



はてなブックマーク に保存 Pocket に保存 Facebook でシェア
Twitter でツイート Twitter をフォロー LINE で送る
rss1.0 取得ボタン RSS 管理者情報 プライバシーポリシー