分割コンパイルによる再定義、オーバーロードエラーがでます。

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
LisetteLander

分割コンパイルによる再定義、オーバーロードエラーがでます。

#1

投稿記事 by LisetteLander » 14年前

上キーと下キーで角度を操作して弾を飛ばすプログラムです。
そのプログラムを分割したのが以下です。
main.cpp

コード:

#include "load.h"

int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int){
        ChangeWindowMode(TRUE), DxLib_Init(), SetDrawScreen( DX_SCREEN_BACK ); //ウィンドウモード変更と初期化と裏画面設定


        int Handle[3];     // 画像格納用ハンドル
        LoadDivGraph( "画像/弾01.png", 3, 3, 1, 14, 16, Handle ); // 3つに画像を分割してロード

        float D_x=320,D_y=240,x=320,y=240,angle=0,speed=1,angle_tmp=0;//初期値デフォx,y,tmpx,y

        // while(裏画面を表画面に反映, メッセージ処理, 画面クリア)
        while( ScreenFlip()==0 && ProcessMessage()==0 && ClearDrawScreen()==0 && gpUpdateKey()==0 ){


//計算フェーズ
                x += cos( angle ) * speed;      // x座標を更新
                y += sin( angle ) * speed;      // y座標を更新

				if( Key[KEY_INPUT_UP]!=0)	angle_tmp -=1/(PI*2);
				if( Key[KEY_INPUT_DOWN]!=0)	angle_tmp +=1/(PI*2);

				if( Key[KEY_INPUT_Z] == 1 ){//zが押されていたら変数の中身を変更
                        x = D_x;        //初期座標にセット
                        y = D_y;
                        speed = 10;      //0.5~2.5の乱数を生成
						angle=angle_tmp;
				}
//描画フェーズ
                DrawRotaGraph( x, y, 1.0, angle+PI/2, Handle[0], TRUE );//弾を描画
				DrawRotaGraph( D_x, D_y, 1.0, angle_tmp+PI/2, Handle[0], TRUE );//弾を描画
                DrawFormatString( 0,0,GetColor(255,255,255), "angle=%.2f, speed=%.2f\n", angle, speed );


		}

		DxLib_End(); // DXライブラリ終了処理
        return 0;
}
load.h

コード:

#ifndef LOAD_DEF //二重include防止
#define LOAD_DEF

#include <math.h>
#include "DxLib.h"
#include "input.h"
int Key[256]; // キーが押されているフレーム数を格納する
#define PI 3.1415926


#endif 
input.h

コード:

#ifndef INPUT_DEF
#define INPUT_DEF

void gpUpdateKey();

#endif

input.cpp

コード:

#include "load.h"
#include "input.h"

// キーの入力状態を更新する
int gpUpdateKey(){
        char tmpKey[256]; // 現在のキーの入力状態を格納する
        GetHitKeyStateAll( tmpKey ); // 全てのキーの入力状態を得る
        for( int i=0; i<256; i++ ){ 
                if( tmpKey[i] != 0 ){ // i番のキーコードに対応するキーが押されていたら
                        Key[i]++;     // 加算
                } else {              // 押されていなければ
                        Key[i] = 0;   // 0にする
                }
        }
        return 0;
}
このようなプログラムをデバックにかけてみたところ
1>.\input.cpp(5) : error C2556: 'int gpUpdateKey(void)' : オーバーロード関数は、'void gpUpdateKey(void)' と戻り値の型のみが異なります。
1> c:\documents and settings\sood\デスクトップ\gameprog\input.h(4) : 'gpUpdateKey' の宣言を確認してください。
1>.\input.cpp(5) : error C2371: 'gpUpdateKey' : 再定義されています。異なる基本型です。
1> c:\documents and settings\sood\デスクトップ\gameprog\input.h(4) : 'gpUpdateKey' の宣言を確認してください。

というエラー(その他型の変換警告)が出ました。
自分でも違和感はあるのですがいまいちどこを直せばいいのかわかりません。
main.cpp

load.h← input.h← input.cpp
↑             ↓
←←←←←←←←←←←

↑自分の頭の中ではこのようになっています。

hidden

Re: 分割コンパイルによる再定義、オーバーロードエラーがでます。

#2

投稿記事 by hidden » 14年前

input.hでは void gpUpdateKey() なのに
input.cppでの実体が int gpUpdateKey() になってます。
これでは同じものとみなされません。

アバター
LisetteLander
記事: 147
登録日時: 14年前
住所: 東京

Re: 分割コンパイルによる再定義、オーバーロードエラーがでます。

#3

投稿記事 by LisetteLander » 14年前

な、なるほど・・
input.hの
void gpUpdateKey()をintにしてみました。
ですが、先ほどまでのエラーはでなくなりましたが
>"int * Key" (?Key@@3PAHA) は既に main.obj で定義されています。
>1 つ以上の複数回定義されているシンボルが見つかりました。
と出ました。
これはload.hがinput.hを経由して二回定義しているからでしょうか?

ここから先はプログラムの設計の問題かと思いますが・・

hidden

Re: 分割コンパイルによる再定義、オーバーロードエラーがでます。

#4

投稿記事 by hidden » 14年前

この辺はまだ自分もごっちゃになりやすいので間違ってたらごめんなさい。

まず、経由して二重に~というのはインクルードガードをしているので大丈夫なはずです。
問題なのはこっちだと思います。
main.cppでload.hをインクルードしています。
よってここには int Key[256] が定義されています。
input.cppでもload.hをインクルードしています。
つまりここでも int Key[256] が定義されてしまっているんです。
同じ名前で二度定義することはできないよ とコンパイラに言われているわけです。

別なソースファイルで定義された変数を利用するにはextern指定子をつける必要があります。
なので、いろいろなソースファイルからインクルードするヘッダは書き方を気をつける必要があります。
自分的にはload.hに int Key[256] が存在するのがまずいかな、と思います。

http://dixq.net/rp/5.html
あたりを参考にして読んでみると回避方法がわかりますよ。

アバター
LisetteLander
記事: 147
登録日時: 14年前
住所: 東京

Re: 分割コンパイルによる再定義、オーバーロードエラーがでます。

#5

投稿記事 by LisetteLander » 14年前

key[256];
の定義が2回されていたことが原因かと思います。
→            →         ↓

load.h   ←→   input.h → input.cpp
↓             ↓
main.cpp  ←     ←

そこで考えたのですが
load.h→            ↓

input.h→input.cpp    hoka.h →hoka.cpp
↓                ↓
main.cpp
と、load.hで定義したものを一方通行でinput.hを経由してmain.cppにはinput.hをincludeさせるという方法にしました。
たとえばここでhoka.hを新たに作ったとしても
hoka.hにはload.hをincludeさせて
main.cppにhoka.hをincludeさせることで作業が捗るのでは?
と思いました。
こうすることでload.hで一回しか定義されず、それを各ファイルが使い回し最終的にmain.cppに回ってくる・・・
と考えたのですが・・・

box
記事: 2002
登録日時: 14年前

Re: 分割コンパイルによる再定義、オーバーロードエラーがでます。

#6

投稿記事 by box » 14年前

安直な方法として…

load.hを

コード:

#ifndef LOAD_DEF //二重include防止
#define LOAD_DEF
 
#include <math.h>
#include "DxLib.h"
#include "input.h"
#ifdef MAIN
#define EXTERN
#else
#define EXTERN extern
#endif
EXTERN int Key[256]; // キーが押されているフレーム数を格納する
#define PI 3.1415926
 
 
#endif
っていう風に修正して、
main.cppの先頭付近「だけ」に
#define MAIN
という行を加えてみる、という手があるかもしれません。
有識者の方からブーイングが出そうな方法ではありますが…。
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

hidden

Re: 分割コンパイルによる再定義、オーバーロードエラーがでます。

#7

投稿記事 by hidden » 14年前

そのまま修正して続ける方法としてはboxさんの仰っている方法が楽かなあ と私は思います。
externして要る時点であっちゃこっちゃから変更できたりと、
問題点はあるわけですが・・・

他に、ここのサイトの龍神録プログラミングの館的には
int Key[256]をinput.cppだけで持って、その内容を知るための関数を用意して
その関数を他のファイルから使う感じですね。

アバター
LisetteLander
記事: 147
登録日時: 14年前
住所: 東京

Re: 分割コンパイルによる再定義、オーバーロードエラーがでます。

#8

投稿記事 by LisetteLander » 14年前

hidden様、ありがとうございます。
extern宣言とマクロ置換のプリプロセッサで書きなおしてみた結果
こうなりました。
main.cpp

コード:

#define GLOBAL_INSTANCE
#include "input.h"


int ProcessLoop(){
	if(ScreenFlip()!=0)return -1;
	if(ProcessMessage()!=0)return -1;
	if(ClearDrawScreen()!=0)return -1;
	if(gpUpdateKey()!=0)return-1;
	return 0;
}

int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int){
        ChangeWindowMode(TRUE), DxLib_Init(), SetDrawScreen( DX_SCREEN_BACK ); //ウィンドウモード変更と初期化と裏画面設定
		

		int Handle[3];     // 画像格納用ハンドル
        LoadDivGraph( "画像/弾01.png", 3, 3, 1, 14, 16, Handle ); // 3つに画像を分割してロード

        float D_x=320,D_y=240,x=320,y=240,angle=0,speed=1,angle_tmp=0;//初期値デフォ x,y tmp x,y

        while(ProcessLoop()==0){


//計算フェーズ
                x += cos( angle ) * speed;      // x座標を更新
                y += sin( angle ) * speed;      // y座標を更新

				if( Key[KEY_INPUT_UP]!=0)	angle_tmp -=1/(PI*2);
				if( Key[KEY_INPUT_DOWN]!=0)	angle_tmp +=1/(PI*2);

				if( Key[KEY_INPUT_Z] == 1 ){//zが押されていたら変数の中身を変更
                        x = D_x;        //初期座標にセット
                        y = D_y;
                        speed = 10;
						angle=angle_tmp;
				}


//描画フェーズ
                DrawRotaGraph( x, y, 1.0, angle+PI/2, Handle[0], TRUE );//弾を描画
				DrawRotaGraph( D_x, D_y, 1.0, angle_tmp+PI/2, Handle[0], TRUE );//弾基準を描画
                DrawFormatString( 0,0,GetColor(255,255,255), "angle=%.2f, speed=%.2f\n", angle, speed );


		}

		DxLib_End(); // DXライブラリ終了処理
        return 0;
}
load.h

コード:

#ifndef LOAD_DEF //二重include防止
#define LOAD_DEF

#ifdef GLOBAL_INSTANCE
#define GLOBAL
#else
#define GLOBAL extern 
#endif

GLOBAL int Key[256]; // キーが押されているフレーム数を格納する
#include <math.h>
#include "DxLib.h"

#define PI 3.1415926

#endif
input.cpp

コード:

// キーの入力状態を更新する
int gpUpdateKey(){
        char tmpKey[256]; // 現在のキーの入力状態を格納する
        GetHitKeyStateAll( tmpKey ); // 全てのキーの入力状態を得る
        for( int i=0; i<256; i++ ){ 
                if( tmpKey[i] != 0 ){ // i番のキーコードに対応するキーが押されていたら
                        Key[i]++;     // 加算
                } else {              // 押されていなければ
                        Key[i] = 0;   // 0にする
                }
        }
        return 0;
}
input.h

コード:

#include "load.h"

#ifndef INPUT_DEF
#define INPUT_DEF

GLOBAL int gpUpdateKey();

#endif

1>.\input.cpp(4) : error C3861: 'GetHitKeyStateAll': 識別子が見つかりませんでした
1>.\input.cpp(7) : error C2065: 'Key' : 定義されていない識別子です。
1>.\input.cpp(9) : error C2065: 'Key' : 定義されていない識別子です。
となりました。。
input.cppにkey[256]の宣言が届いていないといういことですよね?
ということは、key[256]はload.hで宣言されていて、input.hを通じてinput.cppに伝わってきていないということ?

アバター
LisetteLander
記事: 147
登録日時: 14年前
住所: 東京

Re: 分割コンパイルによる再定義、オーバーロードエラーがでます。

#9

投稿記事 by LisetteLander » 14年前

box さんが書きました:安直な方法として…

load.hを

コード:

#ifndef LOAD_DEF //二重include防止
#define LOAD_DEF
 
#include <math.h>
#include "DxLib.h"
#include "input.h"
#ifdef MAIN
#define EXTERN
#else
#define EXTERN extern
#endif
EXTERN int Key[256]; // キーが押されているフレーム数を格納する
#define PI 3.1415926
 
 
#endif
っていう風に修正して、
main.cppの先頭付近「だけ」に
#define MAIN
という行を加えてみる、という手があるかもしれません。
有識者の方からブーイングが出そうな方法ではありますが…。
無知なもので・・すいません、3つ質問していいですか?
>#include "input.h"
>#ifdef MAIN
この部分で、input.h内にexternを仕様する関数があった場合、不都合はないですか?
>main.cppの先頭付近「だけ」に
だけに、っていうことはマクロを取り消す?ということでしょうか?
でも行に加えるってことだから・・・?
>#include "input.h"
相互にファイルを#includeするとエラーになるようなならないような・・・

すいません、参考にされたコードが一番最初のでしたね・・
自分の中で問題作ってしまった・・
最後に編集したユーザー LisetteLander on 2011年6月18日(土) 21:34 [ 編集 1 回目 ]

box
記事: 2002
登録日時: 14年前

Re: 分割コンパイルによる再定義、オーバーロードエラーがでます。

#10

投稿記事 by box » 14年前

input.cpp

input.h
をインクルードしていないのはどうしてですか?
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

box
記事: 2002
登録日時: 14年前

Re: 分割コンパイルによる再定義、オーバーロードエラーがでます。

#11

投稿記事 by box » 14年前

sood さんが書きました: >main.cppの先頭付近「だけ」に
だけに、っていうことはマクロを取り消す?ということでしょうか?
今なさっている
GLOBAL_INSTANCE
と同じことですよ。
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

アバター
LisetteLander
記事: 147
登録日時: 14年前
住所: 東京

Re: 分割コンパイルによる再定義、オーバーロードエラーがでます。

#12

投稿記事 by LisetteLander » 14年前

load.hの
#include <math.h>
#include "DxLib.h"
を指定のとおり入れ替え、
input.cppでinput.hをincludeしたら正常に動きました・・・
なぜ・・・

>今なさっている
>GLOBAL_INSTANCE
>と同じことですよ。
あ、なるほど

アバター
うしお
記事: 56
登録日時: 14年前

Re: 分割コンパイルによる再定義、オーバーロードエラーがでます。

#13

投稿記事 by うしお » 14年前

インクルード順番がどう、マクロがどうという話というよりは、
リンケージの話ではないでしょうか?
http://www7b.biglobe.ne.jp/~robe/cpphtml/
このあたりが参考になると思います。

hidden

Re: 分割コンパイルによる再定義、オーバーロードエラーがでます。

#14

投稿記事 by hidden » 14年前

インクルードガードやら何やらに惑わされて混乱されているようですので、
まず最小限のmain.cppを用意して分離していくことからやったほうがいいように思います。

このままだと、さまざまな関数や変数が広域に存在してゴタゴタなプログラムになっていきそうですので。

box
記事: 2002
登録日時: 14年前

Re: 分割コンパイルによる再定義、オーバーロードエラーがでます。

#15

投稿記事 by box » 14年前

うしお さんが書きました:インクルード順番がどう、マクロがどうという話というよりは、
リンケージの話ではないでしょうか?
質問者さんが当時引っかかってらっしゃったのはコンパイルエラーに関することですから、
リンクに行く前のことでありました。
バグのないプログラムはない。
プログラムは思ったとおりには動かない。書いたとおりに動く。

アバター
LisetteLander
記事: 147
登録日時: 14年前
住所: 東京

Re: 分割コンパイルによる再定義、オーバーロードエラーがでます。

#16

投稿記事 by LisetteLander » 14年前

hidden さんが書きました:インクルードガードやら何やらに惑わされて混乱されているようですので、
まず最小限のmain.cppを用意して分離していくことからやったほうがいいように思います。

このままだと、さまざまな関数や変数が広域に存在してゴタゴタなプログラムになっていきそうですので。
そうですね・・・、もう一度最初から分離して、同じ結果になるか、どうしてなるのかを検討してみたいと思います。
BOXさんもありがとうございました!

閉鎖

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