ライブラリのようなものをつくりたい

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
アバター
MoNoQLoREATOR
記事: 284
登録日時: 13年前
住所: 東京

ライブラリのようなものをつくりたい

#1

投稿記事 by MoNoQLoREATOR » 12年前

ある一つのヘッダファイルをインクルードすることで、そのヘッダファイルに定義されている様々な関数(やクラス等)を扱うことができるようにしたいと考えています。

正確には、「関数(やクラス等)が定義されているヘッダファイル」へのインクルード文が記述されている「ある一つのヘッダファイル」を読み込むことで実現させたいという状況です。

しかし以下のようにすると『main.obj : error LNK2005: "void __cdecl funcB(void)" (?funcB@@YAXXZ) は既に hoge.obj で定義されています』というエラーが出てしまいます。


main.cpp

コード:

#include "head.h"

void main(){
	funcA();
	funcB();
	funcC();
}
hoge.cpp

コード:

#include "head.h"
//関数定義など
head.h

コード:

#include "A.h"
#include "B.h"
#include "C.h"
A.h

コード:

#pragma once

#include "B.h"

void funcA(){}
B.h

コード:

#pragma once

void funcB(){}
C.h

コード:

#pragma once

void funcC(){}

どうしたらよいのでしょうか。
ご教授よろしくお願い致します。
最後に編集したユーザー MoNoQLoREATOR on 2012年2月23日(木) 19:15 [ 編集 1 回目 ]

アバター
nullptr
記事: 239
登録日時: 12年前

Re: ライブラリのようなものをつくりたい

#2

投稿記事 by nullptr » 12年前

・・・・・?

コード:

#pragma once
 
void funcB(){}
で中身を定義していますが、hoge.cppでは何を定義しているのですか?
 
 
✜ で C ご ✜
: す + 注 :
¦ か + 文 ¦
?
Is the は :
order C++? ✜
     糸冬   
  ――――――――
  制作・著作 NHK
 
 

アバター
MoNoQLoREATOR
記事: 284
登録日時: 13年前
住所: 東京

Re: ライブラリのようなものをつくりたい

#3

投稿記事 by MoNoQLoREATOR » 12年前

>>loweさん
「エラーが出てしまうケースとして、このようなケースがありますが、どうすればよいでしょう?」と聞くためだけにhoge.cppをつくったので、実際には何も定義していません。(hoge.cppでhead.hをインクルードしなければこのエラーは起こりません。つまりこのエラーは、複数のファイルでhead.hがインクルードされた場合に発生するようです。)

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: ライブラリのようなものをつくりたい

#4

投稿記事 by softya(ソフト屋) » 12年前

例えば

コード:

#pragma once
 
#include "B.h"
 
void funcA(){}
だと funcA()の実体の定義ということになるので、このヘッダを複数のcppにインクルードすると多重定義になります。

[補足]
ヘッダだけインクルードして、プログラムを組み込んでしまう方法としては
1.本当にライブラリにしてしまう。
2.定義か参照か切り分ければ良いので龍神録のようにGLOBAL_INSTANCEの定義有無で切り分ける。
3.クラスの宣言だけを行いインスタンスをヘッダでは生成しない。つまりヘッダには単なる関数の定義は書かない。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

たかぎ
記事: 328
登録日時: 13年前
住所: 大阪
連絡を取る:

Re: ライブラリのようなものをつくりたい

#5

投稿記事 by たかぎ » 12年前

ややバッドノウハウ的ではありますが...
ヘッダファイルの中で定義する関数は、すべてインライン関数か関数テンプレートにしましょう。
それで解決するはずです。

アバター
MoNoQLoREATOR
記事: 284
登録日時: 13年前
住所: 東京

Re: ライブラリのようなものをつくりたい

#6

投稿記事 by MoNoQLoREATOR » 12年前

>>softya(ソフト屋)さん
>>たかぎさん

返信ありがとうございます。
とりあえずソフト屋さんの2番目の方法で解決してみることにしました。
しかし、以下のようにしてみても、同じエラーが出ました。

main.cpp

コード:

#define FW_FUNCTION
#include "head.h"

void main(){
	funcA();
	funcB();
	funcC();
}
hoge.cpp

コード:

#include "head.h"
//関数定義など
head.h

コード:

#ifdef FW_FUNCTION
#define FUNCTION
#else
#define FUNCTION extern
#endif

#include "A.h"
#include "B.h"
#include "C.h"
A.h

コード:

#pragma once

#include "B.h"

FUNCTION void funcA(){}
B.h

コード:

#pragma once

FUNCTION void funcB(){}
C.h

コード:

#pragma once

FUNCTION void funcC(){}

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: ライブラリのようなものをつくりたい

#7

投稿記事 by softya(ソフト屋) » 12年前

ちがいますよ。
私の書き方たが悪かったですかね。

コード:

#ifdef FW_FUNCTION
// 実体
void funcA(){
	//	実際の関数の処理
}
#else
//プロトタイプ
void funcA();
#endif
って事です。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

たかぎ
記事: 328
登録日時: 13年前
住所: 大阪
連絡を取る:

Re: ライブラリのようなものをつくりたい

#8

投稿記事 by たかぎ » 12年前

FUNCTIONマクロは何の意味もありませんね。
そうではなく...

B.h

コード:

#ifndef B_H
#define B_H

inlin void funcB(){}

#endif
のようにしましょうということです。

アバター
nullptr
記事: 239
登録日時: 12年前

Re: ライブラリのようなものをつくりたい

#9

投稿記事 by nullptr » 12年前

MoNoQLoREATOR さんが書きました:>>loweさん
「エラーが出てしまうケースとして、このようなケースがありますが、どうすればよいでしょう?」と聞くためだけにhoge.cppをつくったので、実際には何も定義していません。
ああ、そうでしたか。すみません。

たかぎさんがインライン化を勧めているのでそれでもいいと思いますが、そもそもライブラリにするならヘッダファイルに実体を作ってしまうこと自体あまりよろしく無いと思います。
ヘッダファイルに実体を作らなければいい話ですし、hoge.cppをサンプルに用意したという事はヘッダに全て詰め込まなくてはいけないという状況でも無いと思いますから。


あと、単なる誤記でしょうが

コード:

#ifndef B_H
#define B_H
 
inline void funcB(){}
 
#endif
ですね
 
 
✜ で C ご ✜
: す + 注 :
¦ か + 文 ¦
?
Is the は :
order C++? ✜
     糸冬   
  ――――――――
  制作・著作 NHK
 
 

アバター
MoNoQLoREATOR
記事: 284
登録日時: 13年前
住所: 東京

Re: ライブラリのようなものをつくりたい

#10

投稿記事 by MoNoQLoREATOR » 12年前

>>softya(ソフト屋)さん
>>たかぎさん
>>loweさん
返信ありがとうございます。

たかぎさんにインライン関数を進めて頂いておりますが、あまり使う気分になりません。インライン関数を調べてみたのですが、#defineを使用したときのように、該当部分がコンパイル前に置き換えられるてしまうのですよね?なんだか非効率的な気がします。

それと、私は「ライブラリのようでライブラリでない物」を作る予定です。ライブラリと言うと堅苦しいイメージがあるので・・・。気分の問題です。

さて、下記のように修正してみるとエラーは出なくなりました。
解決しました。本当にありがとうございました。

main.cpp

コード:

#define FW_FUNCTION
#include "head.h"

void main(){
	funcA();
	funcB();
	funcC();
}
hoge.cpp

コード:

#include "head.h"
//関数定義など
head.h

コード:

#include "A.h"
#include "B.h"
#include "C.h"
A.h

コード:

#pragma once
#include "B.h"

#ifdef FW_FUNCTION
void funcA(){}
#else
void funcA();
#endif
B.h

コード:

#pragma once
#ifdef FW_FUNCTION
void funcB(){}
#else
void funcB();
#endif
C.h

コード:

#pragma once
#ifdef FW_FUNCTION
void funcC(){}
#else
void funcC();
#endif

beatle
記事: 1281
登録日時: 12年前
住所: 埼玉
連絡を取る:

Re: ライブラリのようなものをつくりたい

#11

投稿記事 by beatle » 12年前

MoNoQLoREATOR さんが書きました: たかぎさんにインライン関数を進めて頂いておりますが、あまり使う気分になりません。インライン関数を調べてみたのですが、#defineを使用したときのように、該当部分がコンパイル前に置き換えられるてしまうのですよね?なんだか非効率的な気がします。
使いたくないなら使わないでまったく問題は有りません.
ただ,インライン関数がいつも非効率かというと,まったくそうでは有りません.

例えば,

コード:

class A
{
    int x_, y_;
public:
    A() : x_(), y_() {}
    int get_x() const { return x_; }
};
というクラスを考えます.
クラスの中で関数を定義しますと,暗黙にインライン化を要請することになりますので,get_x関数はほとんどの場合インライン化されます.
もし,get_xがインライン化されない場合,get_xの呼び出しは例えば
  1. 戻り番地をスタックに積む
  2. thisポインタをスタックに積む
  3. get_xにジャンプ(ジャンプするので,CPUのパイプラインはフラッシュされてしまう)
  4. thisポインタにx_のオフセットを加算して,そのアドレスからデータをロード
  5. ロードした値をeaxレジスタに格納
  6. get_xから戻る
などという手順を踏むことになります.get_xがインライン化されれば,この手順のうち1, 2, 3, 6の手順をすっ飛ばすことができます.結構効率が良くなります.

インライン化されるとコードサイズが肥大化してしまう,という懸念はまさにその通りです.インライン化はコードサイズを少し増やす代わりに,実行速度を上げることができる可能性がある技術です.

たかぎ
記事: 328
登録日時: 13年前
住所: 大阪
連絡を取る:

Re: ライブラリのようなものをつくりたい

#12

投稿記事 by たかぎ » 12年前

beatle さんが書きました:インライン化されるとコードサイズが肥大化してしまう,という懸念はまさにその通りです.インライン化はコードサイズを少し増やす代わりに,実行速度を上げることができる可能性がある技術です.
実はそうでもないんですよ。
引数のコピーや関数のコール&リターンのコードが、関数の実体のサイズを上回るときはもちろん、そうでない場合でもインライン関数のほうが小さくなることがあります。

というのは、関数を呼び出すと、その関数から例外が送出される可能性を考慮しないといけないわけですが、インライン関数の場合は中が丸見えですので、例外が送出される可能性がまったくなければそのことがコンパイラにわかります。
結果として、例外にからんだコードが生成されなくても済むようになります。

あるいは、関数の枠を超えた最適化ができますので、極端な場合、関数の処理をコンパイル時に解決してしまうことさえできるのです。

もっとも、何でもかんでもインライン関数にすればよいというわけではありません。
テンプレートと組み合わせれば、インラインではない関数もヘッダファイルに定義を記述することが可能になります。
ヘッダファイルに定義を全部入れることの便利さは、Boost C++ Librariesなどのテンプレートライブラリを使ったことがある人なら容易に理解できるはずです。

アバター
MoNoQLoREATOR
記事: 284
登録日時: 13年前
住所: 東京

Re: ライブラリのようなものをつくりたい

#13

投稿記事 by MoNoQLoREATOR » 12年前

>>beatleさん
>>たかぎさん

非効率的だという言い方には語弊がありましたね。
お言葉をお借りしますと、「コードの肥大化」が気に入らないというのが理由です。
関数があまりにも短い場合はインライン関数にする予定ですよ。

閉鎖

“C言語何でも質問掲示板” へ戻る