C++ help, please!

Discussion in 'Civ4 - SDK/Python' started by Ninja2, May 27, 2008.

  1. Ninja2

    Ninja2 Great Engineer

    Joined:
    Nov 17, 2005
    Messages:
    1,142
    Location:
    Denmarkia
    Okay, so I'm trying to get my feet wet with C++. :crazyeye:

    I'll be posting a few questions in this thread, whenever they pop up. And guess what, one just did! :D

    This is a function from CvPlot.cpp:

    Code:
    CvUnit* CvPlot::getBestDefender(PlayerTypes eOwner, PlayerTypes eAttackingPlayer, const CvUnit* pAttacker, bool bTestAtWar, bool bTestPotentialEnemy, bool bTestCanMove) const
    {
    	CLLNode<IDInfo>* pUnitNode;
    	CvUnit* pLoopUnit;
    	CvUnit* pBestUnit;
    
    What do the asterisk signs symbolize? I believe they are pointers, but I don't understand the context...

    More questions: Why the 'e' in front of Owner and AttackingPlayer? And what are all the 'p's in front of other statements?
     
  2. moopoo

    moopoo King

    Joined:
    Jan 5, 2008
    Messages:
    729
    Location:
    Adelaide, Australia
    From what I've seen in the many and varied tutorials i've read of python and C++, the p's and e's and c's and the rest of the lower case letters infront of things are referring to what type of thing it is. For example, something with a b in front is boolean, i think p refers to either player or pointer, etc.
     
  3. PeteT

    PeteT Warlord

    Joined:
    Jan 2, 2002
    Messages:
    273
    Location:
    Winterpeg
  4. Ninja2

    Ninja2 Great Engineer

    Joined:
    Nov 17, 2005
    Messages:
    1,142
    Location:
    Denmarkia
    Awesome! Thanks, PeteT! :goodjob: eOwner must be enum Owner, then.
     
  5. CivFanCCS

    CivFanCCS Warlord

    Joined:
    Jun 1, 2006
    Messages:
    126
    Location:
    NY

    Ninja,

    You are right, they are pointers. Basically a pointer is an address location to some kind of memory.

    For example:
    CvUnit* pLoopUnit;

    says to the OS, set aside a piece of memory large enough to hold an object called CvUnit and store the location in the variable pLoopUnit. This does not actually create the memory location, just reserves a spot in the heap big enough to hold a CvUnit object. This looks like the start of a loop, so the programmer is looping through a list of CvUnit objects and for each loop storing the address of that object in pLoopUnit.
     
  6. Ninja2

    Ninja2 Great Engineer

    Joined:
    Nov 17, 2005
    Messages:
    1,142
    Location:
    Denmarkia
    Hey, excellent timing on your answer, CivFanCCS, 'cause I got another question! :D

    Code:
    bool CvUnit::interceptTest(const CvPlot* pPlot)
    {
    	CvUnit* pInterceptor = bestInterceptor(pPlot);
    This is the beginning of the function which determines if an air unit is intercepted in it's mission. So the function involves two units, and the function also uses variables from both the attacking plane and the interceptor. So my question is: pInterceptor is declared, so if I want to use a variable from the interceptor, I would use e.g.

    Code:
    if (pInterceptor->getDomainType() == DOMAIN_AIR)
    Do I need to declare CvUnit* pAttacker as well, or is it implied that if I don't use pInterceptor, then the variables from the attacker is used?
     
  7. xienwolf

    xienwolf Deity

    Joined:
    Oct 4, 2007
    Messages:
    10,589
    Location:
    Location! Location!
    Since you are in CvUnit, it means you are currently "inside" the stats of one of the units. In this case, you are in the Attacker. When you say something like "pInterceptor->getDomainType()", it means you are bookmarking your current place inside the CvUnit data of the attacker, and popping over into the CvUnit data of the Interceptor to get some information from him.
     
  8. Ninja2

    Ninja2 Great Engineer

    Joined:
    Nov 17, 2005
    Messages:
    1,142
    Location:
    Denmarkia
    Right, but after I've gotten the information from the pInterceptor, if I use an int like evasionProbability without a pointer, I'm still asking for data from the attacker, right?

    Thanks for the help! :)
     
  9. xienwolf

    xienwolf Deity

    Joined:
    Oct 4, 2007
    Messages:
    10,589
    Location:
    Location! Location!
    If you use an INT then it will be whatever you stored there. So your question doesn't quite give the level of information required to answer it.


    If you stated:

    int iEvasionProbability = pInterceptor->getEveasionProbability();

    then that INT will always mean the one you loaded from the Interceptor. If you stated simply:

    int iEvasionProbability = getEveasionProbability();

    Then it would mean the Attacker, since you are in his CvUnit reference frame.


    If you used:

    m_iEvasionProbability

    Then that is bad form, even though that number is all you get when you access getEvasionProbability(). In general you should only have one function which directly reads any variable, and only one function which directly changes it. Then you funnel everything else through there. This makes it easier to avoid complications in the future when you modify things (like if you made it so that having a certain new Resource type could enhance your Evasion chances, then you could have the getEvasionProbability() function add that bonus into the answer it returns and instantly update all of your code everywhere, since all of them call to that function ultimately)



    If you instead mean to ask if once you use a pInterceptor-> type of call that becomes your permanent new reference frame, then no, it does not. To get a new value from the Interceptor you have to declare it the same way again:

    pInterceptor->getDomainType();
    pInterceptor->getEvasionProbability();

    That is the form you would have to use in order to ask for a second value from the interceptor. Using:

    pInterceptor->getDomainType();
    getEvasionProbability();

    Will get the Interceptor's Domain, but the Attacker's Evasion Chances.
     
  10. Ninja2

    Ninja2 Great Engineer

    Joined:
    Nov 17, 2005
    Messages:
    1,142
    Location:
    Denmarkia
    Thanks! This was the answer I was looking for! :) It's tricky asking the question in the precise manner, when one does not fully understand C++.
     
  11. Ninja2

    Ninja2 Great Engineer

    Joined:
    Nov 17, 2005
    Messages:
    1,142
    Location:
    Denmarkia
    Another question from CvUnit.cpp:

    The function CvUnit::joinGroup has a line which looks like this in 3.17:

    Code:
    if ((pNewSelectionGroup == NULL) || canJoinGroup(pPlot, pNewSelectionGroup))
    In the Road to War mod, the same line looks like this:

    Code:
    if ((pNewSelectionGroup == NULL) || canJoinGroup(plot(), pNewSelectionGroup))
    The difference is subtle - the 3.17 code uses a pPlot, the RtW code uses a plot(). Are the functions identical?
     
  12. EmperorFool

    EmperorFool Deity

    Joined:
    Mar 2, 2007
    Messages:
    9,633
    Location:
    Mountain View, California
    pPlot is a variable--not a function call. All function calls in C++ will have parentheses after the function's name (or a variable pointing to the function's address) that hold zero or more arguments being passed to the function. In the case of "plot()", no arguments are being passed.

    In the normal code, if you look up a few lines you'll see this:

    Code:
    pPlot = plot();
    
    and pPlot doesn't change in the lines in between. Assuming the other function calls between the two lines don't alter the plot the current unit is standing on, the two quoted lines are equivalent.

    I'm not looking at the RtW code, and it's possible that it doesn't have the line I quoted above. In that case, plot() makes sense. Otherwise the change in RtW is a minor de-optimization.
     
  13. Ninja2

    Ninja2 Great Engineer

    Joined:
    Nov 17, 2005
    Messages:
    1,142
    Location:
    Denmarkia
    Firaxis have made some changes to the code from 3.17 to 3.19 that I don't fully understand. For example, in 3.17 there is this type of line in many header files:

    DllExport int getNumUnitInfos();

    in 3.19, the same line is simply:

    int getNumUnitInfos();

    What has changed? And more importantly, if I have merged code using the old syntax, should this be changed (i.e., delete the "DllExport " from these lines)?
     
  14. killmeplease

    killmeplease Mk Z on Steam

    Joined:
    Nov 22, 2007
    Messages:
    2,794
    Location:
    Samara
    Much of code was moved from python scripts to SDK because of performance reasons
    DllExport means that getNumUnitInfos was a python function in 3.17
     
  15. Ninja2

    Ninja2 Great Engineer

    Joined:
    Nov 17, 2005
    Messages:
    1,142
    Location:
    Denmarkia
    Thanks! What about the second part of my question? :)
     
  16. xienwolf

    xienwolf Deity

    Joined:
    Oct 4, 2007
    Messages:
    10,589
    Location:
    Location! Location!
    DllExport had nothing to do with Python. That allowed the EXE to utilize the function, and modification to how a function is called (or removal of it) which the EXE uses will cause a crash. Thus DllExport indicates a function which you must be careful with, AS LONG AS IT IS ONLY USED WHEN NEEDED. Someone was just nice enough to run through and remove it from (hopefully all) functions which don't actually get used from the EXE.


    There may also be a slight performance enhancement by not showing the EXE things it doesn't need to see, so ideally you will also remove these extra lines from your own code. But you will cause yourself a VERY annoying to debug crash if you remove it from a line which you should not have removed from, so be careful about pruning them if you do decide to.
     
  17. EmperorFool

    EmperorFool Deity

    Joined:
    Mar 2, 2007
    Messages:
    9,633
    Location:
    Mountain View, California
    DllExport is what makes a DLL a DLL. Let's start first with an EXE--an application. It has a single entry point, in C and C++ this is

    Code:
    int main(int argv, char** argv)
    {
        ...
    }
    
    When you launch an application, the operating system (Windows, Mac, Linux, etc) looks in a specific place for this one function. The C/C++ compiler looks for the above function and places it in this specific place. By specific I mean that each operating system defines how it wants an executable (EXE) to be packaged.

    DLLs are different in that they have many entry points (functions) that can be called externally. Again, each operating system defines how these functions should be exposed. They are typically organized into a lookup table from name to location (offset memory address into the file). The more functions you export, the larger the table becomes, increasing file size and runtime cost (slightly as xienwolf pointed out).

    Removing DllExport from a function only removes the function's entry from the lookup table.
     
  18. Ninja2

    Ninja2 Great Engineer

    Joined:
    Nov 17, 2005
    Messages:
    1,142
    Location:
    Denmarkia
    Next project that I'm working on is an old mod by TheLopez, where buildings can convert resources to other resources. I've got his old mod working, and I am trying to expand it, so that more than one type of resources is needed to create an output. All the xml work is done, and that part works fine. However, the buildings are not making the output they should, even with abundant input resources. Here is the code I'm using, the old code has been commented out just above/below my new code. Can anyone spot any errors?

    Spoiler :
    Code:
    void CvCity::processBuildingBonuses()
    {
    	int iI, iJ, iBuildingProducedBonusAmount, iTempRequiredInputBonusValue, iTempAndRequiredInputBonusValue, iCityBonusAmount, iCityAndBonusAmount;
    
    	int iK = 0;
    
    	bool bBuildingProducedBonus = false;
    
    	bool* pabBuildingProcessed = new bool[GC.getNumBuildingInfos()];
    
    	for(iI=0;iI<GC.getNumBuildingInfos();iI++)
    	{
    		pabBuildingProcessed[iI] = false;
    	}
    
    	iBuildingProducedBonusAmount = 99999;
    
    	resetBuildingOutputBonuses();
    
    	for(iK = GC.getNumBuildingInfos();iK >= 0; iK--)
    	{
    		for(iI = 0;iI<GC.getNumBuildingInfos(); iI++)
    		{
    			if(pabBuildingProcessed[iI])
    			{
    				continue;
    			}
    
    			if(!getNumActiveBuilding((BuildingTypes)iI) > 0)
    			{
    				pabBuildingProcessed[iI] = true;
    				continue;
    			}
    
    			for(iJ = 0;iJ<GC.getNumBonusInfos();iJ++)
    			{
    				if(!GC.getBuildingInfo((BuildingTypes)iI).isRequiredInputBonus((BonusTypes)iJ))
    				{
    					continue;
    				}
    				
    				if(!GC.getBuildingInfo((BuildingTypes)iI).isAndRequiredInputBonus((BonusTypes)iJ))
    				{
    					continue;
    				}
    
    				//iCityBonusAmount = getNumBonuses((BonusTypes)iJ);
    				iCityBonusAmount = getNumBonuses((BonusTypes)iJ);
    				iCityAndBonusAmount = getNumBonuses((BonusTypes)iJ);
    
    				//if(iCityBonusAmount == 0)
    				if(iCityBonusAmount == 0 || iCityAndBonusAmount == 0)
    				{
    					bBuildingProducedBonus = false;
    					break;
    				}
    
    				iTempRequiredInputBonusValue = GC.getBuildingInfo((BuildingTypes)iI).getRequiredInputBonusValue((BonusTypes)iJ);
    				iTempAndRequiredInputBonusValue = GC.getBuildingInfo((BuildingTypes)iI).getAndRequiredInputBonusValue((BonusTypes)iJ);
    
    				//if((iCityBonusAmount/iTempRequiredInputBonusValue) == 0)
    				if((iCityBonusAmount/iTempRequiredInputBonusValue) == 0 || (iCityAndBonusAmount/iTempAndRequiredInputBonusValue) == 0)
    				{
    					bBuildingProducedBonus = false;
    					break;
    				}
    
    				//if((iCityBonusAmount/iTempRequiredInputBonusValue) < iBuildingProducedBonusAmount)
    				if((iCityBonusAmount/iTempRequiredInputBonusValue) < iBuildingProducedBonusAmount && (iCityAndBonusAmount/iTempAndRequiredInputBonusValue) < iBuildingProducedBonusAmount)
    				{
    					//iBuildingProducedBonusAmount = iCityBonusAmount/iTempRequiredInputBonusValue
    					iBuildingProducedBonusAmount = std::min((iCityBonusAmount/iTempRequiredInputBonusValue),(iCityAndBonusAmount/iTempAndRequiredInputBonusValue));
    					bBuildingProducedBonus = true;
    				}
    			}
    
    			if(iBuildingProducedBonusAmount > 0 && bBuildingProducedBonus)
    			{
    				for(iJ = 0;iJ<GC.getNumBonusInfos();iJ++)
    				{
    					if(!GC.getBuildingInfo((BuildingTypes)iI).isBuildingOutputBonus((BonusTypes)iJ))
    					{
    						continue;
    					}
    					m_paiBuildingOutputBonuses[iJ] = iBuildingProducedBonusAmount*GC.getBuildingInfo((BuildingTypes)iI).getBuildingOutputBonusValues((BonusTypes)iJ);
    					//changeNumBonuses((BonusTypes)iJ, m_paiBuildingOutputBonuses[iJ]);
    					changeFreeBonus((BonusTypes)iJ, m_paiBuildingOutputBonuses[iJ]);
    					
    				}
    				pabBuildingProcessed[iI] = true;
    			}
    
    			bBuildingProducedBonus = false;
    			iBuildingProducedBonusAmount = 99999;
    		}
    	}
    	SAFE_DELETE_ARRAY(pabBuildingProcessed);
    }
    
     
  19. Ninja2

    Ninja2 Great Engineer

    Joined:
    Nov 17, 2005
    Messages:
    1,142
    Location:
    Denmarkia
    I suppose I should ask a more precise question. :) In the code posted above, I have:

    iCityBonusAmount = getNumBonuses((BonusTypes)iJ);
    iCityAndBonusAmount = getNumBonuses((BonusTypes)iJ);

    The two i's refer to different bonus resources, which are read from CIVBuildingsInfo.xml. My first question is if getNumBonunses((BonusTypes)iJ); does what I expect -- that is, look up the amount of those two different bonus resources available in that city? Or do I need one line to have iJ, and the other... iK??
     
  20. God-Emperor

    God-Emperor Deity

    Joined:
    Jul 18, 2009
    Messages:
    3,551
    Location:
    Texas
    No, they don't.

    Since you didn't change it between the two uses there is a very simple mathematical statement that is true: iJ = iJ.
    Since that is true, getNumBonuses((BonusTypes)iJ) = getNumBonuses((BonusTypes)iJ).
    And it follows that iCityBonusAmount = iCityAndBonusAmount.

    You need to consider what you are trying to do...

    I expect you'll need to change things that happen before that point (and probably after too) - the two cases that bail out if (BonusTypes)iJ) is not required via the two different requirements. The BonusType iJ will only pass both (skipping both continue statments) if both the Required and the RndRequired are set to that same resource.

    Instead of the one loop over all resources, you might change that to an "if" that checks two flags. Before that "if" initialize two flags to true. Then you loop over all the resoruces and do two checks. The first checks to see if it is the plain required resource and if you have any - if it is required and you don't have it set flag 1 to be false, the second does the same for the "and" required resoruce. (Soing it this way, with the default set to true, takes care of the case where there is no such requirement).

    Ot something along those lines.
     

Share This Page