Thread: Static variable length arrays

  1. #1
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607

    Static variable length arrays

    Why does this work?

    Code:
    class CFoo
    {
      static const int Array[];
    };
    But if you remove the static modifier, it fails to compile.

  2. #2
    (?<!re)tired Mario F.'s Avatar
    Join Date
    May 2006
    Location
    Ireland
    Posts
    8,446
    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.

  3. #3
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    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?

  4. #4
    (?<!re)tired Mario F.'s Avatar
    Join Date
    May 2006
    Location
    Ireland
    Posts
    8,446
    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.

    Code:
    class Foo {
        static std::vector<int> bar;
    }
    And (usually) on the companion cpp file, you define them:

    Code:
    std::vector<int> Foo::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.

    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:

    Code:
    class Foo {
        static const std::vector<int> bar;
    }
    
    //foo.cpp
    std::vector<int> Foo::bar(bar2);  //where bar2 is a vector defined somewhere
    or

    Code:
    class Foo {
        static const std::vector<int> bar;
    }
    
    //foo.cpp
    std::vector<int> Foo::bar(5);  //5 dimensions all initialized to 0
    The last is obviously pretty much useless.
    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.

  5. #5
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    >> 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.

  6. #6
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    So why am I getting unresolved external reference when I attempt to use an STL container?

  7. #7
    Registered User
    Join Date
    Oct 2001
    Posts
    2,934
    >So why am I getting unresolved external reference when I attempt to use an STL container?
    We're not mindreaders.

  8. #8
    (?<!re)tired Mario F.'s Avatar
    Join Date
    May 2006
    Location
    Ireland
    Posts
    8,446
    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.

  9. #9
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Noted. Here it is:

    ScriptCmn.h
    Code:
    #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.h
    Code:
    #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() {  }
    
    };
    CGameObject.cpp

    Code:
    #include "stdafx.h"
    #include "CGameObject.h"
    
    
    
    BEGIN_SCRIPT_MSGMAP(CGameObject)
      ON_SCRIPT_CMD1(ID_TEST,&TestIt)
    END_SCRIPT_MSGMAP
    Errors
    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 TestIt() is what I was looking for to see if it would actually try to call it. It does which means the setup works.

    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:

    Code:
    void CGameObject::SetupMessageMap(void)
    {
      AddHandler(ID_TEST,SFSig_Param1_n,Test);
    }
    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.

    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.

  10. #10
    Registered User
    Join Date
    Oct 2001
    Posts
    2,934
    > 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.

  11. #11
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    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.

  12. #12
    (?<!re)tired Mario F.'s Avatar
    Join Date
    May 2006
    Location
    Ireland
    Posts
    8,446
    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)

    Code:
    class Foo {
             static const int size = 5; // declared and initialized. 
             double bar[size];
    };
    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.

    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.

  13. #13
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    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:
    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); \
    } \
    So now we should be on our way to message handling. Thanks all for your help.

    How would you initialize a std::map like this?
    Last edited by VirtualAce; 09-14-2006 at 03:15 AM.

  14. #14
    (?<!re)tired Mario F.'s Avatar
    Join Date
    May 2006
    Location
    Ireland
    Posts
    8,446
    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.

  15. #15
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    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.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. scope of global variable vs. static
    By chiefmonkey in forum C++ Programming
    Replies: 4
    Last Post: 06-21-2009, 12:23 PM
  2. Code review
    By Elysia in forum C++ Programming
    Replies: 71
    Last Post: 05-13-2008, 09:42 PM
  3. Static variable or function parameter?
    By Abda92 in forum C Programming
    Replies: 1
    Last Post: 12-02-2007, 12:37 PM
  4. get keyboard and mouse events
    By ratte in forum Linux Programming
    Replies: 10
    Last Post: 11-17-2007, 05:42 PM
  5. Variable Allocation in a simple operating system
    By awkeller in forum C Programming
    Replies: 1
    Last Post: 12-08-2001, 02:26 PM