C++編で扱っている C++ は 2003年に登場した C++03
という、とても古いバージョンのものです。C++ はその後、C++11 -> C++14
-> C++17 -> C++20 -> C++23 と更新されています。
なかでも 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_BEGINint 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 typechar 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 typechar 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 typechar 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 typechar 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)
{
(a);
write}
void write_string(const char* a)
{
(a);
write}
このように、関数テンプレート版の write関数を呼び出します。write_c.h があれば、C言語のプログラムからでも、int型と const char*型だけに限って、write関数を使うことができるようになりました。
/* main.c */
#include "write_c.h"
int main(void)
{
(100);
write_int("abc");
write_string
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 が代入されます。
章の内容を全面的に変更して新規作成。
Programming Place Plus のトップページへ
はてなブックマーク に保存 | Pocket に保存 | Facebook でシェア |
X で ポスト/フォロー | LINE で送る | noteで書く |
RSS | 管理者情報 | プライバシーポリシー |