FNEW and adding classes to the DLL

monju125

Chieftain
Joined
Apr 18, 2015
Messages
68
Curious if we have anyone who has mastered FNEW and added new, complex classes to the DLL. I can get a basic class (that is to say, the class only contains a constructor and primitive types) instantiated and working, but once I start to expand the class beyond that, it all goes to hell and the pointer is immediately invalidated. I Googled and searched this site for any mentions of FNEW or adding a new class and only found two real discussions on it: One where Drawmeus asked about it and responded to himself by saying, "This is hard"; and another that was only tangentially related and the answer didn't have anything to do with FNEW.

Specifically, I added a dummy class to CvPlayer in a separate header file:
Code:
class Dummy
{
public:
	int dummyInt;
	Dummy();
};

Dummy::Dummy()
{
	dummyInt = 1;
}

CvPlayer.h
Code:
	Dummy* dummyClass;

In CvPlayer.cpp::gameStartInit(). Using c_eCiv5GamePlayDLL didn't seem to work, so I use c_eMPoolTypeUserStart + 0x500 to avoid conflicts.
Code:
	dummyClass = FNEW(Dummy(), c_eMPoolTypeUserStart + 0x500, 0);
	dummyClass->dummyInt = 2;

This all goes well; dummyClass is instantiated, dummyInt is initially set to 1 and gets changed to 2 when gameStartInit runs and the pointer's reference persists after this. As soon as I add a dummy struct like the one below to the class, it refuses to instantiate properly. I've tried instantiating the struct with FNEW as well and it exhibits the same behavior.
Code:
	struct dummyStruct
	{
		int dummyStructInt;
	};
	dummyStruct newDummyStruct;

Code:
Dummy::Dummy()
{
	dummyInt = 1;
	newDummyStruct.dummyStructInt = 2;
}

I'm at a complete loss, and without being able to figure this out, the project I'm working on isn't worth continuing.
 
Bumping since I posted it right before the migration. Anyone have any idea? There has to be a way to use FNEW to allocate and use new classes.
 
FNEW is a firaxis macro which adds memory debugging info when the DLL is compiled as a debug build. In normal builds it is defined as such

Code:
#define	FNEW( type, mpool, tag ) new type

It wants the type, not the function pointer to the constructor. An example usage from existing code which has a struct as a member of a class is

Code:
m_pAdvisorCounsel = FNEW(CvAdvisorCounsel, c_eCiv5GameplayDLL, 0);
 
That's odd because there are numerous calls in the DLL where it passes a default constructor to FNEW instead of just the type. For example, in CvAIOperation:

Code:
CvAIOperation* CvAIOperation::CreateOperation(AIOperationTypes eAIOperationType, PlayerTypes ePlayer)
{
	switch(eAIOperationType)
	{
	case AI_OPERATION_BASIC_CITY_ATTACK:
		return FNEW(CvAIOperationBasicCityAttack(), c_eCiv5GameplayDLL, 0);
	case AI_OPERATION_DESTROY_BARBARIAN_CAMP:
		return FNEW(CvAIOperationDestroyBarbarianCamp(), c_eCiv5GameplayDLL, 0);
	case AI_OPERATION_FOUND_CITY:
		return FNEW(CvAIOperationFoundCity(), c_eCiv5GameplayDLL, 0);
	case AI_OPERATION_MERCHANT_DELEGATION:
		return FNEW(CvAIOperationMerchantDelegation(), c_eCiv5GameplayDLL, 0);
	case AI_OPERATION_CONCERT_TOUR:
		return FNEW(CvAIOperationConcertTour(), c_eCiv5GameplayDLL, 0);
	case AI_OPERATION_NAVAL_BOMBARDMENT:
		return FNEW(CvAIOperationNavalBombardment(), c_eCiv5GameplayDLL, 0);
	case AI_OPERATION_NAVAL_SUPERIORITY:
		return FNEW(CvAIOperationNavalSuperiority(), c_eCiv5GameplayDLL, 0);
	case AI_OPERATION_PURE_NAVAL_CITY_ATTACK:
		return FNEW(CvAIOperationPureNavalCityAttack(), c_eCiv5GameplayDLL, 0);
	case AI_OPERATION_COLONIZE:
		return FNEW(CvAINavalEscortedOperation(), c_eCiv5GameplayDLL, 0);
	case AI_OPERATION_QUICK_COLONIZE:
		return FNEW(CvAIOperationQuickColonize(), c_eCiv5GameplayDLL, 0);
	case AI_OPERATION_PILLAGE_ENEMY:
		return FNEW(CvAIOperationPillageEnemy(), c_eCiv5GameplayDLL, 0);
	case AI_OPERATION_CITY_CLOSE_DEFENSE:
		return FNEW(CvAIOperationCityCloseDefense(), c_eCiv5GameplayDLL, 0);
	case AI_OPERATION_RAPID_RESPONSE:
		return FNEW(CvAIOperationRapidResponse(), c_eCiv5GameplayDLL, 0);
	case AI_OPERATION_SNEAK_CITY_ATTACK:
	{
		if(GC.getGame().getGameTurn() < 50 && GET_PLAYER(ePlayer).GetDiplomacyAI()->GetBoldness() >= 5)
		{
			return FNEW(CvAIOperationQuickSneakCityAttack(), c_eCiv5GameplayDLL, 0);
		}
		return FNEW(CvAIOperationSneakCityAttack(), c_eCiv5GameplayDLL, 0);
	}
	case AI_OPERATION_SMALL_CITY_ATTACK:
		return FNEW(CvAIOperationSmallCityAttack(), c_eCiv5GameplayDLL, 0);
	case AI_OPERATION_CITY_STATE_ATTACK:
		return FNEW(CvAIOperationCityStateAttack(), c_eCiv5GameplayDLL, 0);
	case AI_OPERATION_NAVAL_ATTACK:
		return FNEW(CvAIOperationNavalAttack(), c_eCiv5GameplayDLL, 0);
	case AI_OPERATION_NAVAL_SNEAK_ATTACK:
		return FNEW(CvAIOperationNavalSneakAttack(), c_eCiv5GameplayDLL, 0);
	case AI_OPERATION_CITY_STATE_NAVAL_ATTACK:
		return FNEW(CvAIOperationNavalCityStateAttack(), c_eCiv5GameplayDLL, 0);
	case AI_OPERATION_NUKE_ATTACK:
		return FNEW(CvAIOperationNukeAttack(), c_eCiv5GameplayDLL, 0);
	}

	return 0;
}

I'm also pretty sure (but it's been a while, so I can't be 100%) that I also tried passing it just the type as I'd seen it called that way as well and neither seemed to work. Also, I'm not building it as Debug, but as Mod, so I'm not sure that applies. As well, trying to just add a class without using FNEW produced the same truncation, so something else is going on there.
 
Code:
return FNEW(CvAIOperationBasicCityAttack(), c_eCiv5GameplayDLL, 0);

will expand to

Code:
new CvAIOperationBasicCityAttack();

which will allocate space for an instance of the CvAIOperationBasicCityAttack class and then explicitly initialise it by calling the no parameter (ie () ) constructor

Code:
return FNEW(CvAIOperationBasicCityAttack, c_eCiv5GameplayDLL, 0);

will expand to

Code:
new CvAIOperationBasicCityAttack;

which will allocate space for an instance of the CvAIOperationBasicCityAttack class and then implicitly initialise it by calling the no parameter (ie () ) constructor.

Functionally, they are identical.

See the "Initializing object allocated with new" section here
 
Right, I know that, I was pointing out exactly that: it doesn't matter if you pass it the default constructor explicitly. I'm still not getting an answer on why it doesn't seem to work either way.
 
Sorry, I think I misread the replies and had kind of a dense moment here. I guess this post is honestly less about FNEW as you've made me realize it's just a macro whose literal only purpose is to call new, but about adding classes to the DLL. It doesn't seem to matter how I attempt to instantiate a new class, unless I instantiate it locally within a function, then it seems to work (only within that function of course). Otherwise, it either isn't allocating the class or it's being cleaned up between being instantiated and attempting to use it. I can't imagine it's going out of scope; I'm adding it as a member of CvPlayer and then attempting to use it within CvPlayer's functions.
 
I am able to get my new class instantiated in CvGlobals though, so that will do as a workaround for now. I'm still curious why I can't seem to get it to work in CvPlayer.
 
For reference for anyone who might find this thread while Googling a similar issue:

This whole thing resolved to being a non-issue. What was leading me astray was when I would attach to the process with breakpoints where my new class was being instantiated or used, it would appear in the IDE that the memory being referenced was invalid. However, outputting the values to a log file showed that it was actually working, it just didn't seem like it during debugging. You of course don't even have to use FNEW since in normal execution it's literally just a wrapper for "new".
 
Back
Top Bottom