[Dev] Some C++ questions about DLL development

dbkblk

Emperor
Joined
Oct 28, 2005
Messages
1,786
Location
France
Hi !

Could someone help me with DLL development ? While trying to import Nightinggale's code into our DLL to get "unicode" support, i've found some notations that i don't know how to read.

Q1:
Code:
iID &= 0x7F
About "&=", does that mean 0x7F is the pointer address of iID ?

Q2:
Code:
i<GC.getNumLanguageInfos()) ? &GC.getLanguageInfo(i)
What does the "?" mean in that part of the code ?

Q3:
What is an "FAssert()" in the code ?

Q4:
Why some "CvGlobals" methods are prefixed with DLLExport ? What does that do in the code ?

Q5:
Same question for "friend class" ?

Q6:
I have some difficulties to understand how the bindings between python and the DLL work. In Nightinggale's code, there are no "cvInternalsGlobals" as he develops on Colonization, so prefixes "gc. " seem to make reference to CvGlobals. However, on our code, each time i reference "gc.", it seems to look for "CvInternalsGlobals". EDIT: "#define GC cvInternalGlobals::getInstance()"
Does this means all the variables in "CvInt..." are the ones usable in Python ? I've never studied language bindings before... :(

I have uploaded the full source code on github (rev870) but i can't compile it. It asks to define a function in CvInternalsGlobals, but if i do that, the dll won't link at the end of the compilation.

Thank you for any help.
 
Q1:
Code:
iID &= 0x7F
About "&=", does that mean 0x7F is the pointer address of iID ?
http://www.somacon.com/p125.php

Q2:
Code:
i<GC.getNumLanguageInfos()) ? &GC.getLanguageInfo(i)
What does the "?" mean in that part of the code ?
A = B ? C : D;
That is the same as writing
Code:
if (B)
{
  A = C;
}
else
{
  A = D;
}
The ?: setup baffles most people the first time they see it, myself included. It's a neat way of writing something in just one line and it can be used as arguments lie
Code:
Function(A > B ? C : D)
That will use either C or D as function argument, depending on A > B.

Q3:
What is an "FAssert()" in the code ?
FAssert tells the exe that you assume (assert) that the argument is true. If you enable asserts in your DLL (either by debug build or assert in makefile 2), a failed assert will freeze the game and bring up a window, which tells which assert failed. It gives the option to ignore or debug (debug only works on debug builds). In other words it's a great way of making the game itself check for bugs.

Vanilla uses it all the time in the functions, which returns a value from an array. It asserts the index to be inside the array.

FAssert and FAssertMsg does the same, except the latter displays a string. Example:
Code:
FAssertMsg(a >= 0; CvString::format("a is %d, but can't be negative", a).c_str());
Regular "string" strings works too, but being able to add variables can be interesting.

A regular release DLL will ignore all assert lines.

Q4:
Why some "CvGlobals" methods are prefixed with DLLExport ? What does that do in the code ?
DllExport makes the function accessible to the game exe. Don't change number of arguments if the function is exported (you can't tell the exe how to call) and don't export your own functions as the exe will never call them anyway.

Q5:
Same question for "friend class" ?
A friend can access protected and private functions and variables.

Q6:
I have some difficulties to understand how the bindings between python and the DLL work. In Nightinggale's code, there are no "cvInternalsGlobals" as he develops on Colonization, so prefixes "gc. " seem to make reference to CvGlobals. However, on our code, each time i reference "gc.", it seems to look for "CvInternalsGlobals". EDIT: "#define GC cvInternalGlobals::getInstance()"
Does this means all the variables in "CvInt..." are the ones usable in Python ? I've never studied language bindings before... :(
GC is the same in BTS as it is in BTS. I don't think I made any calls to python as it would make little sense in this context.
 
Q2:
Code:
i<GC.getNumLanguageInfos()) ? &GC.getLanguageInfo(i)
What does the "?" mean in that part of the code ?

Nightinggale already explained what it does. I'll just add that this a ? b : c thingy is usually called the ternary operator.
 
Q1:
Code:
iID &= 0x7F
About "&=", does that mean 0x7F is the pointer address of iID ?

Writing
Code:
iID &= 0x7F
is equivalent to


Code:
iID = iID & 0x7F
, which means compute the new value of iID by taking the old value of iID and performing a bitwise AND with 0x7F.
 
Thank you both for all the explanations. I am very grateful to you!
I'll continue to merge Unicode support and keep you informed :)
 
I bring this thread back from the dead: Is there someone who knows where can I find the function which refers to the graphical display of a unit / building at the bottom left part of the screen? It has been moved in AND compared to the base game, and I don't know where to find it.

EDIT: Nevermind. I found it in CvMainInterface.py at screen.addUnitGraphicGFC("InterfaceUnitModel"...
 
Is there a way to make the widget help popups larger? The sethelptextarea doesn't increase the width...
 
Hello, coming from Caveman2Cosmos forum to here, I'm asking:

At SVN revision 391 (2010-09-19) by @Afforess, he removed DllExport from CvGame:: setAIAutoPlay (See CvGame.h), which is imported by the exe.

Should we correct this? --> By providing the original function alongside our new one.
Code:
DllExport void setAIAutoPlay(int iNewValue);
Afforess' setAIAutoPlay takes different arguments, but we can take advantage of function overloading to provide compatibility.


I checked with Dependency Walker and fortunately, I see your core dll has only 1 mismatch with the exe's imports, which is this.

Here is my original post, https://forums.civfanatics.com/threads/modders-documentation.441325/page-15#post-15475510

@f1rpo cleaned unused DllExport's for his AdvCiv mod. You may follow him in doing so, too.
 
Last edited:
If the exe tries to call a function in the dll and it's missing, then the game will crash. Because of this, don't remove DllExport functions unless confirmed with Dependency Walker that they are in fact not used.
Another issue is changing arguments. The exe will call with the vanilla arguments. This might cause issues if the DLL expects different arguments. Assigning default values to new arguments is NOT a workaround for this issue.

If you want to overload a function, which the exe can call, then call it something else and make the DllExport function contain a call to your new overloaded function. If you do not ensure the exe can freely call the functions it expects to call, then you risk ending up with a crashing game.
 
I was too early to suggest using overloading. By reviewing how the dll code CvGame:: setAIAutoPlay takes 2 additional arguments, we may probably be able to restore it to its origin.
Code:
void setAIAutoPlay(PlayerTypes iPlayer, int iNewValue, bool bForced = false);
I think if iPlayer and bForced can somehow be referred to in the global context (ie. save the variables elsewhere), we can reduce these two parameters and match the exe's import function signature.
 
My mod also adds a parameter to CvGame::setAIAutoPlay. I always use this pattern for adding parameters to exported functions:
Code:
DllExport void setAIAutoPlay(int iNewValue) {
    setAIAutoPlay(iNewValue, true);
}
void setAIAutoPlay(int iNewValue, bool bChangePlayerStatus);
CvGame.h#L254

This makes the whole issue invisible in the .cpp file.
 
Last edited:
Forgive me guys, it's a long time I've been away from modding so I just noticed these posts. Are you saying that we should change the code in CvGame.h from

Code:
    void setAIAutoPlay(PlayerTypes iPlayer, int iNewValue, bool bForced = false);

to

Code:
DllExport void setAIAutoPlay(int iNewValue) {
   setAIAutoPlay(iPlayer, iNewValue, false);
}
void setAIAutoPlay(PlayerTypes iPlayer, int iNewValue, bool bForced);

?

Is there a reason for doing so? I've never been a very experienced modder so I'm not sure I understand the reason for this change (if this is what you are meaning). Thanks.
 
The problem is that the exe will call with the arguments, which were required when the exe was compiled. In this case it's a single int. When we add arguments to the dll, the dll code assumes the caller to provide more data, but we can't teach the exe to do that, meaning the extra arguments will contain garbage data.

The way to get around this issue is to not change the functions the exe can call. Instead we can add new functions, which are only called by the dll. The exe called function can then call the dll only function. This way the arguments will be set correctly.

Default arguments won't help here because they are used by the caller as "if you don't type an argument here, compile like you wrote 0 or whatever the default value is". This means we need to recompile the exe to teach it the default value.
 
Top Bottom