分割コンパイルについて

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
wasawasa
記事: 94
登録日時: 11年前

分割コンパイルについて

#1

投稿記事 by wasawasa » 11年前

自作のプログラムを試験的に次のように分割してビルドしてみたら以下のようなエラーメッセージが表示されてしまいました。
main.cpp

コード:

#include "DxLib.h"
#include "Anim.h"
//配列の生成に変数は使えない
//関数の外では変数に値を代入できない
//配列は定義時に値を代入する事
		int TimeCount;//全体の時間カウント

int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int){
        ChangeWindowMode(TRUE), DxLib_Init(), SetDrawScreen( DX_SCREEN_BACK ); //ウィンドウモード変更と初期化と裏画面設定
//int Color ;
//Color = GetColor( 255 , 255 , 255 ) ;//test
		TimeCount=0;	
		PictureRead();
        // while( 裏画面を表画面に反映, メッセージ処理, 画面クリア )
        while( ScreenFlip()==0 && ProcessMessage()==0 && ClearDrawScreen()==0 ){
//DrawFormatString( 0, 0, Color, "TimeCount=%d", TimeCount ) ;//test
			Obj_Syutugen(TimeCount);
			Anim_AllDraw(TimeCount);
			TimeCount++;
        }
        
        DxLib_End(); // DXライブラリ終了処理
        return 0;
}  
Anim.cpp

コード:

#include "DxLib.h"
#include "Anim.h"
#include "Obj.h"
#define Anim_longest_Picture 5//最も多い枚数の画像を使うアニメの使用画像枚数
#define ObjApMax 3//ObjApの要素の数
#define SpriteAmount 12//スプライトの総数
#define PictureReadAmount 2//読み込む画像ファイルの数

		static int Object_Gr[SpriteAmount];//画像格納変数
		static double Object_point[SpriteAmount][2]={//各画像の原点
			{20,20},
			{20,20},
			{20,20},
			{20,20},
			{20,20},
			{20,20},
			{20,20},
			{20,20},
			{20,20},
			{20,20},
			{20,20},
			{20,20}
		};

		struct Anim{
			int Picture[Anim_longest_Picture];//アニメに使う画像番号 [アニメ番号][使用画像]
			int Frame[Anim_longest_Picture];//アニメの各画像の表示フレーム [アニメ番号][フレーム数]
		};
		static struct Anim AnimObj[6] = {
			{{0,1,-1,-1,-1},{100,200,-1,-1,-1}},//0:自機ニュートラル
			{{2,3,-1,-1,-1},{10,20,-1,-1,-1}},
			{{4,5,-1,-1,-1},{10,20,-1,-1,-1}},
			{{6,7,-1,-1,-1},{100,200,-1,-1,-1}},
			{{8,9,-1,-1,-1},{10,20,-1,-1,-1}},
			{{10,11,-1,-1,-1},{10,20,-1,-1,-1}}
		};

		struct ObjA{
			int AnimNo,Stcnt,layer;//アニメ番号,アニメ切り替え基準カウンター,表示レイヤー
			int flag;//出現する時間and出現判定
			double x,y;//x座標,y座標
		};
		static struct ObjA ObjAp[ObjApMax] = {//出現しているオブジェクトのデータ(出現していない所は全て-1)
			{-1,-1,-1,	 -1,	-1,-1},
			{-1,-1,-1,	 -1,	-1,-1},
			{-1,-1,-1,	 -1,	-1,-1}
		};

		struct ReadFileInf{
			char *FileName;//ファイルのポインタ
			int allnum,xnum,ynum,xsize,ysize;//画像の分割総数,横に対する分割数,縦に対する分割数,分割された画像一つのx方向の大きさ,y方向の大きさ
		};
		static struct ReadFileInf ReadForObject_Gr[PictureReadAmount] = {
			{ "../Picture/STGtate_test.png" , 6 , 2 , 3 , 40 , 40 },
			{"../Picture/STGtate_test2.png" , 6 , 2 , 3 , 40 , 40 }
		};

		void Obj_Syutugen(int TC_inObj_Syutugen){//オブジェクトの出現管理:ObjAp入力用
			Obj_Syutugen_main(TC_inObj_Syutugen,ObjAp,ObjApMax);
		}

		void PictureRead(){//画像ファイル読み込み 使用外部変数:PictureReadAmount
			int i;
			int a=0;
			for(i = 0; i < PictureReadAmount; i ++){
				LoadDivGraph( ReadForObject_Gr[i].FileName , ReadForObject_Gr[i].allnum , ReadForObject_Gr[i].xnum , ReadForObject_Gr[i].ynum , ReadForObject_Gr[i].xsize , ReadForObject_Gr[i].ysize , Object_Gr + a );
				a += ReadForObject_Gr[i].allnum;
			}
		}

		void Anim_Draw(int ObjectNumber,int TC_inAnimDraw){//アニメ描画 
			int i,j;
			int a[Anim_longest_Picture];//アニメの各画像の終了フレーム
			for(i = 0; i < Anim_longest_Picture; i ++){//aの初期化
				a[i] = 0;
			}
			int b=0;//aの最大値
			int c=0;
			for(i = 0; i < Anim_longest_Picture; i ++){
				for(j = 0; j <= i; j ++){
					if(AnimObj[ObjAp[ObjectNumber].AnimNo].Frame[j] >=0){
						a[i] += AnimObj[ObjAp[ObjectNumber].AnimNo].Frame[j];
					}
					else{
						a[i] = -1;
					}
				}
			}
			for(i = 0;i < Anim_longest_Picture; i ++){
				if(a[i]>b){
					b = a[i];
				}
			}
			c = (TC_inAnimDraw - ObjAp[ObjectNumber].Stcnt)%b;
			for(i = 0;i < Anim_longest_Picture; i ++){
				if(c<a[i]){
					DrawGraph( ObjAp[ObjectNumber].x - Object_point[AnimObj[ObjAp[ObjectNumber].AnimNo].Picture[i]][0], ObjAp[ObjectNumber].y - Object_point[AnimObj[ObjAp[ObjectNumber].AnimNo].Picture[i]][1], Object_Gr[AnimObj[ObjAp[ObjectNumber].AnimNo].Picture[i]], TRUE ); //画像の描画
					break;
				}
			}
		}

		void Anim_AllDraw(int TC_inAnimAllDraw){//定義されたアニメの描画
			int i,j,k;
			int dumy=100000;
			int H=0;
			int KariObj[ObjApMax];//仮想的な出現オブジェクトデータベース
			for(i = 0; i < ObjApMax; i ++){
				KariObj[i]=-1;
			}
			for(j = 0; j < ObjApMax; j ++){
				for(i = 0; i < ObjApMax; i ++){
					if(ObjAp[i].flag>=0){
						if(dumy>=ObjAp[i].layer){
							for(k = 0; k < j; k++){
								if(i==KariObj[k]){
									H=1;
								}
							}
							if(H==0){
								dumy=ObjAp[i].layer;
								KariObj[j]=i;
							}
							H=0;
						}
					}
				}
				H=0;
				dumy=100000;
			}
			for(j = 0; j < ObjApMax; j ++){
				if(KariObj[j] >= 0 && KariObj[j] < ObjApMax){
					Anim_Draw(KariObj[j],TC_inAnimAllDraw);
				}
			}
		}
Obj.cpp

コード:

#include "DxLib.h"
#include "Obj.h"
#define ObjPalMax 3//ObjPalの要素の数

		struct Obj{
			int AnimNo,Stcnt,layer;//アニメ番号,アニメ切り替え基準カウンター,表示レイヤー
			int flag;//出現する時間and出現判定
			double x,y;//x座標,y座標
		};
		static struct Obj ObjPal[ObjPalMax] = {//オブジェクト毎のパラメータ
			{4,0,80,	   0,	100,100},
			{0,0,50,	1000,	110,110},
			{0,0,50,	2000,	150,110}
		};

		void Obj_Syutugen_main(int TC_inObj_Syutugen , struct ObjA *OA_inOSm , int OAM_inOSm){//オブジェクトの出現管理 
			int i,j;
			for(i = 0; i < ObjPalMax; i ++){
				if(TC_inObj_Syutugen == ObjPal[i].flag){
					for(j = 0; j < OAM_inOSm; j++){
						if(OA_inOSm[j].flag == -1){
							OA_inOSm[j].AnimNo = ObjPal[i].AnimNo;
							OA_inOSm[j].flag   = ObjPal[i].flag;
							OA_inOSm[j].layer  = ObjPal[i].layer;
							OA_inOSm[j].Stcnt  = ObjPal[i].Stcnt;
							OA_inOSm[j].x      = ObjPal[i].x;
							OA_inOSm[j].y		= ObjPal[i].y;
							break;
						}
					}
				}
			}
		}
Anim.h

コード:

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

void Obj_Syutugen(int TC_inObj_Syutugen);
void PictureRead();
void Anim_AllDraw(int TC_inAnimAllDraw);

#endif
Obj.h

コード:

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

void Obj_Syutugen_main(int TC_inObj_Syutugen , struct ObjA *OA_inOSm , int OAM_inOSm);

#endif
エラーメッセージ
1>main.cpp
1>Anim.cpp
1>c:\users\aaaa\visual studio 2008\projects\bbbbb\anim.cpp(59) : error C3861: 'Obj_Syutugen_main': 識別子が見つかりませんでした
1>c:\users\aaaa\visual studio 2008\projects\bbbbb\anim.cpp(97) : warning C4244: '引数' : 'double' から 'int' への変換です。データが失われる可能性があります。
1>c:\users\aaaa\visual studio 2008\projects\bbbbb\anim.cpp(97) : warning C4244: '引数' : 'double' から 'int' への変換です。データが失われる可能性があります。
1>Obj.cpp
1>c:\users\aaaa\visual studio 2008\projects\bbbbb\obj.cpp(21) : error C2036: 'ObjA *' : サイズが不明です。
1>c:\users\aaaa\visual studio 2008\projects\bbbbb\obj.cpp(21) : error C2027: 認識できない型 'ObjA' が使われています。
1> c:\users\aaaa\visual studio 2008\projects\bbbbb\obj.h(4) : 'ObjA' の宣言を確認してください。
1>c:\users\aaaa\visual studio 2008\projects\bbbbb\obj.cpp(21) : error C2228: '.flag' の左側はクラス、構造体、共用体でなければなりません
1>c:\users\aaaa\visual studio 2008\projects\bbbbb\obj.cpp(22) : error C2036: 'ObjA *' : サイズが不明です。
1>c:\users\aaaa\visual studio 2008\projects\bbbbb\obj.cpp(22) : error C2027: 認識できない型 'ObjA' が使われています。
1> c:\users\aaaa\visual studio 2008\projects\bbbbb\obj.h(4) : 'ObjA' の宣言を確認してください。
1>c:\users\aaaa\visual studio 2008\projects\bbbbb\obj.cpp(22) : error C2228: '.AnimNo' の左側はクラス、構造体、共用体でなければなりません
1>c:\users\aaaa\visual studio 2008\projects\bbbbb\obj.cpp(23) : error C2036: 'ObjA *' : サイズが不明です。
1>c:\users\aaaa\visual studio 2008\projects\bbbbb\obj.cpp(23) : error C2027: 認識できない型 'ObjA' が使われています。
1> c:\users\aaaa\visual studio 2008\projects\bbbbb\obj.h(4) : 'ObjA' の宣言を確認してください。
1>c:\users\aaaa\visual studio 2008\projects\bbbbb\obj.cpp(23) : error C2228: '.flag' の左側はクラス、構造体、共用体でなければなりません
1>c:\users\aaaa\visual studio 2008\projects\bbbbb\obj.cpp(24) : error C2036: 'ObjA *' : サイズが不明です。
1>c:\users\aaaa\visual studio 2008\projects\bbbbb\obj.cpp(24) : error C2027: 認識できない型 'ObjA' が使われています。
1> c:\users\aaaa\visual studio 2008\projects\bbbbb\obj.h(4) : 'ObjA' の宣言を確認してください。
1>c:\users\aaaa\visual studio 2008\projects\bbbbb\obj.cpp(24) : error C2228: '.layer' の左側はクラス、構造体、共用体でなければなりません
1>c:\users\aaaa\visual studio 2008\projects\bbbbb\obj.cpp(25) : error C2036: 'ObjA *' : サイズが不明です。
1>c:\users\aaaa\visual studio 2008\projects\bbbbb\obj.cpp(25) : error C2027: 認識できない型 'ObjA' が使われています。
1> c:\users\aaaa\visual studio 2008\projects\bbbbb\obj.h(4) : 'ObjA' の宣言を確認してください。
1>c:\users\aaaa\visual studio 2008\projects\bbbbb\obj.cpp(25) : error C2228: '.Stcnt' の左側はクラス、構造体、共用体でなければなりません
1>c:\users\aaaa\visual studio 2008\projects\bbbbb\obj.cpp(26) : error C2036: 'ObjA *' : サイズが不明です。
1>c:\users\aaaa\visual studio 2008\projects\bbbbb\obj.cpp(26) : error C2027: 認識できない型 'ObjA' が使われています。
1> c:\users\aaaa\visual studio 2008\projects\bbbbb\obj.h(4) : 'ObjA' の宣言を確認してください。
1>c:\users\aaaa\visual studio 2008\projects\bbbbb\obj.cpp(26) : error C2228: '.x' の左側はクラス、構造体、共用体でなければなりません
1>c:\users\aaaa\visual studio 2008\projects\bbbbb\obj.cpp(27) : error C2036: 'ObjA *' : サイズが不明です。
1>c:\users\aaaa\visual studio 2008\projects\bbbbb\obj.cpp(27) : error C2027: 認識できない型 'ObjA' が使われています。
1> c:\users\aaaa\visual studio 2008\projects\bbbbb\obj.h(4) : 'ObjA' の宣言を確認してください。
1>c:\users\aaaa\visual studio 2008\projects\bbbbb\obj.cpp(27) : error C2228: '.y' の左側はクラス、構造体、共用体でなければなりません

エラーメッセージの内容から分割された別のファイルの内容を参照するのに失敗していると言う事は大体見当が付きますが、これをどうやって直したら良いのかが分かりません。
とりあえずファイルを分割する際は
・プログラムの処理の順番はmain関数の頭からスタートし、main関数の最後で終了する。
・基本的に他のファイルの変数や構造体やdefineを参照する事は出来ないので他のファイルのデータを使いたい場合は関数に引数として渡す必要がある。
・main関数のあるファイル以外で変数などを宣言する際は頭にstaticと付ける。
・関数を宣言するファイルと参照するファイルの頭に#include "aaa.h"等とヘッダーを読み込む記述をし、ヘッダーファイルに関数のプロトタイプ宣言をする事で別のファイルの関数を使用する事が出来る。
・ヘッダファイルには二重読み込み防止の記述をする。
・全てのファイルの間で同じ名前の関数や構造体を二つ以上宣言してはいけない。
こんな感じの認識で行っていたのですが、何か間違いはあるでしょうか。
私のプログラムではどこが間違っていてどういう風に直したら良いのか、ファイルを分割する際の私の認識のどこに間違いや至らない点があるのかを教えて頂けると幸いです。
自分でも突っ込み所満載の記述になっている予感がひしひしと感じるのですがよろしくお願いします。

アバター
h2so5
副管理人
記事: 2212
登録日時: 14年前
住所: 東京
連絡を取る:

Re: 分割コンパイルについて

#2

投稿記事 by h2so5 » 11年前

インクルードガードの名前が両方共DEF_PLAYER_Hになっているのが問題では?

wasawasa
記事: 94
登録日時: 11年前

Re: 分割コンパイルについて

#3

投稿記事 by wasawasa » 11年前

あっ・・・・・・(´・ω・`)

と思ってObj.hとAnim.hのインクルードガードの名前をDEF_OBJ_HとDEF_ANIM_Hに変えてみたところ、Anim.cppの59行目のエラーが消えただけでしたのでもっと別の理由で参照できていないのだと思われます。

アバター
へにっくす
記事: 634
登録日時: 13年前
住所: 東京都

Re: 分割コンパイルについて

#4

投稿記事 by へにっくす » 11年前

ObjAという構造体がAnim.cppに宣言されてますが、Obj.cpp、Obj.hでもObjAという構造体を使っている以上、宣言は必要です。
そもそも構造体の宣言はヘッダーで行うべきですが、なぜ.cppで宣言してるのでしょうか?
written by へにっくす

アバター
usao
記事: 1889
登録日時: 12年前
連絡を取る:

Re: 分割コンパイルについて

#5

投稿記事 by usao » 11年前

・Anim.cpp にある struct ObjA
・Obj.cpp にある struct Obj

この2つ,名前以外は同じであるように見えるのですが
(わざわざ)分けている意味は何でしょう?

仮に,両者を分ける必要がない(=同じもののつもり)のだとすれば,

(1)とりあえず新しいヘッダファイルを用意
(2)struct Objの宣言を(1)のヘッダに書く.
(3)Anim.cpp, Obj.cppでは struct Obj,ObjA の宣言をなくし,代わりに(1)のヘッダをincludeする形にする
(4)ObjAとなっている箇所をすべてObjに変更

…とかすれば,すっきりするのではないかと.
オフトピック
>そもそも構造体の宣言はヘッダーで行うべき
…と,一部だけ抜き出すとちょっと誤解を生みそうな表現にも見えますが,
今回の件はそうするのが素直ですね.

アバター
usao
記事: 1889
登録日時: 12年前
連絡を取る:

Re: 分割コンパイルについて

#6

投稿記事 by usao » 11年前

>こんな感じの認識で行っていたのですが、何か間違いはあるでしょうか。

この部分への返信が無いようなので,頑張ってみる.
(誤りや補足があれば お願いします>他の方)

[hr]
>・プログラムの処理の順番はmain関数の頭からスタートし、main関数の最後で終了する。

変数の初期化とかいう話があるけれど,「とりあえずは」そういう認識でも良い…かな
[hr]
>・基本的に他のファイルの変数や構造体やdefineを参照する事は出来ないので他のファイルのデータを使いたい場合は関数に引数として渡す必要がある。

「他のファイル」というのは 異なる.cpp間でのことを言っているのかな?
・構造体の「型の宣言」 や defineマクロ が別の翻訳単位(cppファイル)にあると 当然それは見えない→使えない
・関数の引数や戻り値で「値」をやりとりすること(これは同じファイル内であってもやるよね)
の2つがすこし混じってしまっている気がするけど…
後者の事を言っているなら,基本は関数でやりとりで良いのではないでしょうか.
(外部変数であればextern宣言というのもある.)
[hr]
>・main関数のあるファイル以外で変数などを宣言する際は頭にstaticと付ける。

staticを付けるべきかどうかは場合次第.(staticって何?っていうのを調べよう)
[hr]
>・関数を宣言するファイルと参照するファイルの頭に#include "aaa.h"等とヘッダーを読み込む記述をし、ヘッダーファイルに関数のプロトタイプ宣言をする事で別のファイルの関数を使用する事が出来る。

正しくは,「関数を使うには,関数を使用する箇所から,その関数の(プロトタイプ)宣言が見えていることが必要」かな.
ヘッダファイルを使うのは,単に利便性のため.
(例えば,一つのソースファイルで,main関数よりも下に関数f()の定義を書いて,
 mainからその関数f()を使うためには mainよりも上の方にf()のプロトタイプ宣言を書きますよね.
 このとき,f()の定義がmainとは別のcppにあろうが,それでいける.)
[hr]
>・ヘッダファイルには二重読み込み防止の記述をする。

普通の用途ではそうする.
[hr]
>・全てのファイルの間で同じ名前の関数や構造体を二つ以上宣言してはいけない。

「宣言」であれば,上述のように,行う. というか,行わないといろいろと使えない.
(H.hをA.cppとB.cppでincludeしたら,
 A.cppとB.cppの #include "H.h" の箇所に,H.hの内容全てを書いたことと同じですので.)
関数の「定義」であれば,完全に同じ型で同じ名前のがあると2重定義になるけど,
変数と同じようにstaticを付けるとか,あるいはnamespaceを使うとかで,名前が被っても大丈夫な状況もある.
[hr]
オフトピック
多分,「ヘッダファイルって?」 というのと, 「宣言と定義」 あたりが曖昧なんじゃないかな?

wasawasa
記事: 94
登録日時: 11年前

Re: 分割コンパイルについて

#7

投稿記事 by wasawasa » 11年前

>usaoさん
大変返事が遅れてすみません。
ヘッダファイルの事を検索してみたのですがヘッダファイルってcpp間の関数のプロトタイプ宣言をするだけのファイルではなくデータを格納する場所を新しく作らない記述なら何でも書けるのですね。
それでstatic Objの宣言をObj.hで行いそれ以外を提案してくれた通りにしてみたところ無事コンパイルする事が出来ました。
本当にありがとうございます。

アバター
usao
記事: 1889
登録日時: 12年前
連絡を取る:

Re: 分割コンパイルについて

#8

投稿記事 by usao » 11年前

繰り返しになりますが,ヘッダファイルについては
>H.hをA.cppとB.cppでincludeしたら,
>A.cppとB.cppの #include "H.h" の箇所に,H.hの内容全てを書いたことと同じ
というだけのことです.
この場合,「H.hを用意してA.cppとB.cppでincludeする」という手段を取るのは
A.cppとB.cppとで 同じことを2回書くのが面倒だから.
(そして,将来,c.cppにも同じことが必要になったときに include と書くだけで済むように.)

なので,「ヘッダに何を書ける(書いてもいいか)」という点については,
「ヘッダファイル無しで複数のcppに全く同じことを書いたらどうなるのか(何がOKで,何がダメなのか)」ということと同じですよ.
オフトピック
#include "H.h" は 単にその場所にファイルH.h の中身を持ってくるだけ なので,
↓のようなことだって(やりたくはないが)できる.

コード:

//H.h
  printf( "Hello World\n" );
  return 0;
}

コード:

#include <stdio.h>
int main()
{
#include "H.h"

閉鎖

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