ヘッダファイルの書き方

解決


VC初心者  2003-04-18 10:35:23  No: 51303  IP: [192.*.*.*]

はじめまして。
VC++6.0で、関数を自作し、これをライブラリ化したいと考えています。
さらに、このライブラリにある関数を呼び出したいのですが、そのため
には、ヘッダファイルを書かなければいけないと思うのですが、このヘッダ
ファイルの書き方がぜんぜんわかりません。
この関数を呼び出すのに必要なヘッダファイルの書き方や、ライブラリで
の書き方における注意点など教えていただきたいです。
参考になるホームページでもかまいません。
よろしくお願いします。

編集 削除
YuO  2003-04-18 13:08:37  No: 51304  IP: [192.*.*.*]

> VC++6.0で、関数を自作し、これをライブラリ化したいと考えています。
> さらに、このライブラリにある関数を呼び出したいのですが、そのため
> には、ヘッダファイルを書かなければいけないと思うのですが、このヘッダ
> ファイルの書き方がぜんぜんわかりません。

必要な宣言のみを書けばいいです。
宣言でなく,構造体・クラス等の定義が含まれる場合はインクルードガードする必要があります。
#C++のインライン関数は定義も書く。

example 1)
関数
int func (int, struct bar *);
を宣言し,funcがfuncが利用する構造体
struct bar;
を定義する場合。
---- func.h ----
#ifndef FUNC_HEADER_FILE_
#define FUNC_HEADER_FILE_

struct bar {
    /* メンバ */
};
int func (int, struct bar *);

#endif /* !defined(FUNC_HEADER_FILE_)
---- func.h ----
最初の#ifndef, #defineと最後の#endifのことを,インクルードガードといいます。
無いとどうなるかは,これらを除いて,
#include "func.h"
#include "func.h"
とやってみればわかると思います。

example 2)
関数
int func (int, struct bar *);

struct bar;
を宣言する場合。
---- func.h ----
struct bar;
int func (int, struct bar *);
---- func.h ----
こんどは,struct barの定義が無くても利用できる場合です。
この場合は,定義が存在しないので,インクルードガードは不要です。
#定義が存在しなくても,インクルードガードをしておくのが普通。

example 2のようなのは使い道がない,と思うかもしれませんが,
標準CライブラリのFILEのように,内部を公開する必要がない構造体に関しては,
宣言だけで済ますことができます。

編集 削除
ブタゴリラ  2003-04-18 13:29:12  No: 51305  IP: [192.*.*.*]

そうですねー。
ヘッダーファイルって言うのは、
マクロやプロトタイプの宣言の集合みたいなものですね。

私も、Yuoさんのような、
インクルードガードは付けています。
もし、外部でその関数を使う場合は、

>例

///////////func.c///////////

int prog(void);//プロトタイプ宣言

int prog(void)
{
 //プログラム....
 return 0;
}

/////////////////////////////////

///////////main.c////////////////
#include "func.h"

int main(void)
{
 if(prog()==0) {//func.cに宣言した、外部関数
     return 0;
 return 1;
}


////////////func.h////////////
#ifndef __FUNC_H__
#define __FUNC_H__

extern int prog(void);//externをつけた場合は
                      //モジュール外部で使うという意味で使われる。
                      //基本的には、つけないのとでは、差は無い。

#endif

////////////////////////

のように、extern を初めにつけてやったりします。(つけなくても動作はしますが、基本的にはつけてやることで、
外部でも使うのかが分かりやすい目印のためにつけたりします。

編集 削除
YuO  2003-04-18 13:55:45  No: 51306  IP: [192.*.*.*]

> #endif /* !defined(FUNC_HEADER_FILE_)

あ……コメント閉じ忘れた。プリプロセッサに怒られる……というわけで,正しくは
#endif /* !defined(FUNC_HEADER_FILE_) */
です。


でもって,
> #define __FUNC_H__
は,未定義動作になることを知っておいた方がよいです。

・下線からはじまって,下線または英大文字が続く識別子は全ての利用について予約
・下線からはじまる識別子は,通常及びタグ名前空間でのファイルスコープ識別子として予約
というのが,ISO Cの言い分で,
・下線が二つ続いたり,下線からはじまって英大文字が続く識別子は実装のために全ての利用について予約
・下線からはじまる識別子は,大域名前空間とstd名前空間で,実装が名前として使うために予約
というのがISO C++の言い分です。
ref) ISO/IEC 9899:1999 7.1.3 Reserved identifiers
ref) JIS X3010:1993 7.1.3 予約済み識別子
ref) ISO/IEC 14882:1998 17.4.3.1.2 Global names
なので,下線からはじまる識別子を使うと,場合によってはライブラリが利用している名前と重なって,
エラーが起きる可能性があります。


ちなみに,私は関数にexternは付けませんね。
関数はinlineやstaticが付いていない限り外部結合ですから,付けるのは面倒なだけです。
全て「外部から呼び出せる」ことが前提で,「外部から呼び出せないもの」について,
staticをつけたり(C)無名名前空間に入れたり(C++)します。

編集 削除
VC初心者  2003-04-18 14:02:39  No: 51307  IP: [192.*.*.*]

やってみましたが、だめでした。
やってみた手順は以下のとおりです。
1.プロジェクトでWin32 Static Libraryのワークスペースを作る。
                ↓
2.stdafx.hにTODOのところに書き込む。
    // TODO: プログラムで必要なヘッダー参照を追加してください。
    #ifndef __FUNC_H__
    #define __FUNC_H__

    extern char * getFileName(char *);  //externをつけた場合は
                                        //モジュール外部で使うという意味で使われる。
                                        //基本的には、つけないのとでは、差は無い。

    #endif
3.stdafx.cppにソースを書く。
  // stdafx.cpp : 標準インクルードファイルを含むソース ファイル
  //  MakeLibraryTest1.pch 生成されるプリコンパイル済ヘッダー
  //  stdafx.obj 生成されるプリコンパイル済タイプ情報

  #include "stdafx.h"

  #include<stdio.h>

  char * getFileName();  /* ファイル名取得関数 */
  char * getFileName()
  {
    char * csFileName = "FileName";
    return csFileName;
  }
4.ビルドする。
5.できたlibファイル(stdafx.lib)とヘッダファイル(stdafx.h)を持っ
てきてCのメイン関数より呼び出す。
#include "stdafx.h"
#include <stdio.h>

void main()
{
  char * strFile;
  
  strFile = getFileName();
  printf("%s\n", strFile);
}
6.ビルドする。
そうすると以下のようなエラーが出てくるのです。
リンク中...
test.obj : error LNK2001: 外部シンボル "_getFileName" は未解決です
Debug/test.exe : fatal error LNK1120: 外部参照 1 が未解決です。
link.exe の実行エラー

test.exe - エラー 2、警告 0

どうしたらいいんでしょう?

編集 削除
YuO  2003-04-18 14:53:59  No: 51308  IP: [192.*.*.*]

とりあえず,stdafx.hではなくて,自分で作ったヘッダファイルに書く方がよいです。

でもって,リンクエラーは単にstdafx.libをリンクしていないのが問題だと思います。

編集 削除
VC初心者  2003-04-18 16:43:49  No: 51309  IP: [192.*.*.*]

だめでした。エラーは解決されませんでした。
ヘッダファイルをMyFunc.hに変え、
さらに、リンクにlibファイルを追加しました。
ちなみに、libファイルの名前はVCTest1Library.libとしています。
リンクの方法も一応書いておくと
プロジェクトの設定で、リンクタブを選び、オブジェクト/ライブラリ モジュールの中に上記のライブラリファイルを追加しました。

編集 削除
YuO  2003-04-18 18:07:15  No: 51310  IP: [192.*.*.*]

Cから使うライブラリをC++で作るときには,extern "C"で修飾しないといけません。
#C++には多重定義があるので,とあるルールに従って名前を引き延ばしている。

さらに,Cで作られたライブラリをC++で使う場合も,
関数宣言をextern "C"で修飾するか,
extern "C"ブロック内で宣言する必要があります。

というわけで,先ほどのfunc.hを拡張すると,
---- func.h ----
#ifndef FUNC_HEADER_FILE_
#define FUNC_HEADER_FILE_

#if defined(__cplusplus)
extern "C" {
#endif /* defined(__cplusplus) */

struct bar {
    /* メンバ */
};
int func (int, struct bar *);

#if defined(__cplusplus)
}
#endif /* defined(__cplusplus) */

#endif /* !defined(FUNC_HEADER_FILE_) */
---- func.h ----
となります。

ちなみに,このヘッダファイルは,
ライブラリを生成するソースファイルからも取り込むのが普通です。
そうすることによって,結合を正しく指定できるためです。

編集 削除
ブタゴリラ  2003-04-18 21:25:12  No: 51311  IP: [192.*.*.*]

うわぁー指摘されちゃってるし。(^^; >#define __FUNC_H__
2重下線って駄目でしたっけ?
これは申し訳ない...。

いつの間にそんな物が標準化されたんだ?全く・・・。
やんなってくるなー。
変な制限多いなー、また変数名等で悩まなきゃイカンでわないか。(--;

Yuoさん教えてくれてありがとう♪

編集 削除
YuO  2003-04-18 22:20:04  No: 51312  IP: [192.*.*.*]

> 2重下線って駄目でしたっけ?
> いつの間にそんな物が標準化されたんだ?全く・・・。

えーっと,ANSI Cが1989年,ISO Cが1990年,JIS Cが1993年です。
それから,ANSI C++とISO C++が1998年です。


> 変な制限多いなー、また変数名等で悩まなきゃイカンでわないか。(--;

コンパイラの実装用に残してあるのが_からはじまる変数名ですからね……。
C++のは,cfront時代に__をマングルに使っていたみたいなのでその名残かもしれません。

C99には,_Bool, _Complex, _Imaginaryという_+大文字からはじまる予約語が存在します。
また,__cplusplusというのもあいますし……。

まぁ,識別子の制限はあとはライブラリと同じ関数名とかマクロ名を使うな,程度ですから,
_に頼りすぎないようにすれば問題ないでしょう。

編集 削除
VC初心者  2003-04-19 00:01:51  No: 51313  IP: [192.*.*.*]

解決しました!
そのまんまコピペでやったのですが、ちゃんとライブラリから
関数を呼べました。
感動しています。
ありがとうございました。
ところで、この__cplusplusとか、FUNC_HEADER_FILE_というのは
どうなんでしょうか?適当な名前でいいのですか?
たとえば、__cplusplusを__mycplusplusにするとか、
FUNC_HEADER_FILE_をMYFUNC_HEADER_FILE_にするとか。
どうなんでしょうか?

編集 削除
YuO  2003-04-19 01:47:01  No: 51314  IP: [192.*.*.*]

> ところで、この__cplusplusとか、FUNC_HEADER_FILE_というのは
> どうなんでしょうか?適当な名前でいいのですか?
> たとえば、__cplusplusを__mycplusplusにするとか、
> FUNC_HEADER_FILE_をMYFUNC_HEADER_FILE_にするとか。
> どうなんでしょうか?

__cplusplusというのは,C++言語における定義済み識別子で,
ISO/IEC 14882:1998に準拠している場合,199711Lという値に展開されます。
ちなみに,VC++の場合は準拠していないので1だったりします。
これは,
#define __cplusplus 199711L
という行が先頭にあるような感じになります。
なお,C言語では定義されていません。

なので,defined(__cplusplus)でソースファイルがC++言語かC言語かを判別できます。
ソースファイル中で使うことは滅多にないですが,
ヘッダファイル中ではよく使います。

FUNC_HEADER_FILEというのは,単に私が名前を付けた識別子です。
なので,自由に変更することができます。

編集 削除
VC初心者  2003-04-21 11:57:10  No: 51315  IP: [192.*.*.*]

ありがとうございました。
いろいろ勉強になりました。

編集 削除