Why does this work?
But if you remove the static modifier, it fails to compile.Code:class CFoo { static const int Array[]; };
Why does this work?
But if you remove the static modifier, it fails to compile.Code:class CFoo { static const int Array[]; };
Being static, you have only declared it. Array dimensions are part of the definition only.
Originally Posted by brewbuck:
Reimplementing a large system in another language to get a 25% performance boost is nonsense. It would be cheaper to just get a computer which is 25% faster.
Ok and I assume static STL containers don't work like that because they aren't actually an object until you add something to them?
Not sure if I understand. You mean not being able to declare them as static?
They can. You declare them normally in the class definition as static.
And (usually) on the companion cpp file, you define them:Code:class Foo { static std::vector<int> bar; }
EDIT: Rereading your last question... if you mean defining them as static consts, then... yes and no. I guess if you use the vector<T> vector_1(vector_2) constructor when defining it, should work. So that's a yes. But I honestly don't know if you can do that with a static const definition. And I can't test it right now. No compiler on this machine.Code:std::vector<int> Foo::bar;
But other than that the best you can do is initialize it with the value-initialize constructor vector<T> vector_1(n).
So... it's either:
orCode:class Foo { static const std::vector<int> bar; } //foo.cpp std::vector<int> Foo::bar(bar2); //where bar2 is a vector defined somewhere
The last is obviously pretty much useless.Code:class Foo { static const std::vector<int> bar; } //foo.cpp std::vector<int> Foo::bar(5); //5 dimensions all initialized to 0
Last edited by Mario F.; 09-13-2006 at 06:59 AM.
Originally Posted by brewbuck:
Reimplementing a large system in another language to get a 25% performance boost is nonsense. It would be cheaper to just get a computer which is 25% faster.
>> Ok and I assume static STL containers don't work like that because they aren't actually an object until you add something to them?
STL containers don't work like that because they aren't arrays. They are always objects regardless of whether anything has been added to them. If you define them without calling a constructor, they will be initialized to empty. The array syntax is special to built-in arrays, and in many cases that syntax requires a size to be specified. For STL containers it doesn't matter, because they use constructors that can do whatever they want.
So why am I getting unresolved external reference when I attempt to use an STL container?
>So why am I getting unresolved external reference when I attempt to use an STL container?
We're not mindreaders.
There is nothing much more that can be said about this Bubba. You can declare STL containers as static members of your classes, as you can built-in types and user-defined types.
If you are having linker errors, the error must be somewhere else. And with static data members I could almost bet you are not defining the member in the cpp file, or you think you are and are forgeting the class name scope.
Without any code...
Originally Posted by brewbuck:
Reimplementing a large system in another language to get a 25% performance boost is nonsense. It would be cheaper to just get a computer which is 25% faster.
Noted. Here it is:
ScriptCmn.h
CGameObject.hCode:#pragma once #include <vector> #include "CGameObject.h" ///////////////////////////////// //Script message map structures// ///////////////////////////////// //Forward delcaration of CGameObject; class CGameObject; //SCRIPT_MSG typedef typedef void (CGameObject::*SCRIPT_MSG)(void); //SCRIPT_MSGMAP_ENTRY struct SCRIPT_MSGMAP_ENTRY { UINT nCommandID; UINT nFuncSig; SCRIPT_MSG pfn; //SCRIPT_MSGMAP_ENTRY(UINT nCommandID,UINT nFuncSig,SCRIPT_MSG pfn): //nCommandID(nCommandID),nFuncSig(nFuncSig),pfn(pfn) { } }; //SCRIPT_MSGMAP struct SCRIPT_MSGMAP { const SCRIPT_MSGMAP *pBaseMap; const SCRIPT_MSGMAP_ENTRY *pEntries; }; enum SFuncSigs { SFSig_Param0_n =0, SFSig_Param1_n, //int fxn(DWORD) SFSig_Param2_n, //int fxn(DWORD,DWORD) SFSig_Param3_n //int fxn(DWORD,DWORD,DWORD) }; union ScriptMsgMapFuncs { SCRIPT_MSG pfn; UINT (CGameObject::*pfnParam1_n)(DWORD); UINT (CGameObject::*pfnParam2_n)(DWORD,DWORD); UINT (CGameObject::*pfnParam3_n)(DWORD,DWORD,DWORD); }; #define DECLARE_SCRIPT_MSGMAP() \ public: \ static const void SetupMessageMap(void); \ static const void AddHandler(UINT nID,UINT nSig,SCRIPT_MSG fxn); #define BEGIN_SCRIPT_MSGMAP(theClass) \ const void theClass::AddHandler(UINT nID,UINT nSig,SCRIPT_MSG fxn) \ { \ SCRIPT_MSGMAP_ENTRY *pEntry=new SCRIPT_MSGMAP_ENTRY(); \ pEntry->nCommandID=nID; \ pEntry->nFuncSig=nSig; \ pEntry->pfn=fxn; \ m_vEntries.reserve(m_vEntries.size()+1); \ m_vEntries.push_back(pEntry); \ } \ const void theClass::SetupMessageMap(void) { #define ON_SCRIPT_CMD1(id,memberFxn) \ AddHandler(id,SFSig_Param1_n,(SCRIPT_MSG)memberFxn); #define END_SCRIPT_MSGMAP }
CGameObject.cppCode:#pragma once #include "ScriptCmn.h" //Forward delcaration of CGameObject; #include <vector> class CBaseObject {}; #define ID_TEST 0x0001 class CGameObject:public CBaseObject { protected: DECLARE_SCRIPT_MSGMAP(); public: static std::vector<SCRIPT_MSGMAP_ENTRY *> m_vEntries; UINT ScriptMsgProc(SCRIPTFUNC *pFunc); bool OnScriptMsg(UINT nCommandID); UINT TestIt(DWORD); CGameObject() { } };
ErrorsCode:#include "stdafx.h" #include "CGameObject.h" BEGIN_SCRIPT_MSGMAP(CGameObject) ON_SCRIPT_CMD1(ID_TEST,&TestIt) END_SCRIPT_MSGMAP
The unresolved TestIt() is what I was looking for to see if it would actually try to call it. It does which means the setup works.Linking...
CGameObject.obj : error LNK2001: unresolved external symbol "public: static class std::vector<struct SCRIPT_MSGMAP_ENTRY *,class std::allocator<struct SCRIPT_MSGMAP_ENTRY *> > CGameObject::m_vEntries" (?m_vEntries@CGameObject@@2V?$vector@PAUSCRIPT_MSG MAP_ENTRY@@V?$allocator@PAUSCRIPT_MSGMAP_ENTRY@@@s td@@@std@@A)
CGameObject.obj : error LNK2019: unresolved external symbol "public: unsigned int __thiscall CGameObject::TestIt(unsigned long)" (?TestIt@CGameObject@@QAEIK@Z) referenced in function "public: static void __cdecl CGameObject::SetupMessageMap(void)" (?SetupMessageMap@CGameObject@@SAXXZ)
Debug\TileEngine.exe : fatal error LNK1120: 2 unresolved externals
Build log was saved at "file://e:\MSVC 6 Projects\ZeldaEngine\Debug\BuildLog.htm"
TileEngine - 3 error(s), 0 warning(s)
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
The unresolved external vector baffles me. Even if I do a reserve() on the vector it still ends up being unresolved external.
The diff here is instead of using a static array I want to add to a STL map and then in OnWndMsg() I want to find the handler based on msgID passed to OnWndMsg() and fire off the handler code.
I'm testing it with a vector here to see if it at least compiles and it does, but it wont link.
Maybe I'm missing something simple here but shouldn't I be able to do that in a #define?
My code obviously is ugly but it's a better solution than having a bunch of static pointer to member function pointers sitting around. This way when you add the ON_SCRIPT_CMDx you are actually adding the the function SetupMessageMap().
No it's not pretty and I'm about ready to scrap it, but I wanted to see if we could get it to work. The final output of this code should be:
Oh and swoop...get off my case and chill bud. Just trying to get this sorted out. Personally I don't know of any better way to do it as of yet.Code:void CGameObject::SetupMessageMap(void) { AddHandler(ID_TEST,SFSig_Param1_n,Test); }
This might work:
//foo.cpp
std::vector<int> Foo::bar(5); //5 dimensions all initialized to 0
Last edited by VirtualAce; 09-13-2006 at 01:43 PM.
> static std::vector<SCRIPT_MSGMAP_ENTRY *> m_vEntries;
When you declare something static within a class, it means you actually have to define it later. Mario can probably post an example.
You need to define your vector like any other static variable. Your "This might work" code is an example of that, although you would have to modify it to fit the m_vEntries vector you have. You don't have to specify an initial size unless you know what the size should be, just use the default constructor.
>> m_vEntries.reserve(m_vEntries.size()+1)
This does nothing useful. The same thing will happen if you call push_back by itself. If you are always using push_back to add entires to the vector, then you should definitely not set a size in the constructor when you define it.
I was suspecting this much, given the error the linker was throwing.
Basically, Bubba, when you declared m_vEntries in CGameObject (under CGameObject.h) you didn't define it. Static data members differ from regular members also on this; They are just declared inside the class body. You need to define them later.
Refer to my previous posts on this thread to figure out how within your current code structure. But basically, you are forced to define them outside the class body.
There is one exception:
If they are const, are of integral type, and you initialize them with a constant expression, you can if you choose so, define them in the class body. So it's actually possible to do this (but only because they are const of integral type and the initializers are constant expressions)
The definition of size is the exception to the static data members definition rules. It's a const, it's of integral type and it is being initialized with a constant expression. So it is legal.Code:class Foo { static const int size = 5; // declared and initialized. double bar[size]; };
More interesting is the definition of bar. It is only possible because a static constant of integral type that is defined with a constant expression is itself a constant expression (Stroustrup).
However for all other cases (yours included) the definition of your static data member needs to be done outside the class body. Since class bodies are normally defined inside header files, that is why you normally should define your static data members in a cpp file, in order to avoid multiple definitions.
Last edited by Mario F.; 09-13-2006 at 03:46 PM.
Originally Posted by brewbuck:
Reimplementing a large system in another language to get a 25% performance boost is nonsense. It would be cheaper to just get a computer which is 25% faster.
Ok thanks all. Very good information. I rarely run into these types of problems which is why my exposure to the topic is limited. But I was basically correct in my assumption of why it was not working although I didn't use the correct semantics. The vector is not defined so thus the unresolved external. It should work then if in the BEGIN_SCRIPT_MSGMAP macro I define the vector using the methods shown in this thread.
I hope so b/c I'd like to get past this ugly code and onto what it is supposed to simplify - I.E.: Message handling.
This works:
So now we should be on our way to message handling. Thanks all for your help.Code:#define BEGIN_SCRIPT_MSGMAP(theClass) \ std::vector<SCRIPT_MSGMAP_ENTRY *> theClass::m_vEntries(0); \ const void theClass::AddHandler(UINT nID,UINT nSig,SCRIPT_MSG fxn) \ { \ SCRIPT_MSGMAP_ENTRY *pEntry=new SCRIPT_MSGMAP_ENTRY(); \ pEntry->nCommandID=nID; \ pEntry->nFuncSig=nSig; \ pEntry->pfn=fxn; \ m_vEntries.push_back(pEntry); \ } \
How would you initialize a std::map like this?
Last edited by VirtualAce; 09-14-2006 at 03:15 AM.
Like you did the vector inside BEGIN_SCRIPT_MSGMAP.
You don't actually need the dimension constructor, unless your first access to the vector expects at least one dimension.
std::vector<SCRIPT_MSGMAP_ENTRY *> theClass::m_vEntries; also defines the vector. It has no dimensions and it's waiting for your push_backs.
As such the map would be something like:
Code:std::map<ID, SCRIPT_MSGMAP_ENTRY *> theClass::m_vEntries
Originally Posted by brewbuck:
Reimplementing a large system in another language to get a 25% performance boost is nonsense. It would be cheaper to just get a computer which is 25% faster.
Ahhh so I can get rid of that. Cool. So even though it's declared as static in the class, I can define it in the implementation file w/o getting duplicate symbol stuff?
I need to read up in my books about this static stuff. Been doing too much MFC, DX, and Win32.
You don't know how much this is helping me. With all of this our game project is light years ahead of where it was a couple of days ago. Now we can begin implementing the script and controlling the game logic from the script.
That is powerful when you can go from script to action inside the engine. I'm quite excited about starting the script writing process and the handler's to respond to the messages.
I just need a message pump to pump the messages from the script to the proc function and I should be in business.
And it's nice I bypassed all the problems with function pointers in classes b/c that was giving me a headache.
Pardon me but I must do a little dance now b/c it works. <dance>tadadadadada...tee..dum...tadadada..</dance>
Last edited by VirtualAce; 09-14-2006 at 05:50 AM.