Orbiter-Forum  

Go Back   Orbiter-Forum > Orbiter Space Flight Simulator > Orbiter SDK
Register Blogs Orbinauts List Social Groups FAQ Projects Mark Forums Read

Orbiter SDK Orbiter software developers post your questions and answers about the SDK, the API interface, LUA, meshing, etc.

Reply
 
Thread Tools
Old 01-07-2020, 04:33 PM   #1
Abdullah Radwan
Addon Developer
 
Abdullah Radwan's Avatar
Default Static calls between 2 classes in one library

Hello,

I want to make the system in the attachments.

The custom cargoes inherit the custom cargo interface. The custom cargo interface will make static calls to add the cargoes to the main API.

This how it's done in code:

Custom Cargo Vessel:
Code:
class Custom: public VESSEL4, public CustomCargo {
public:
	Custom(OBJHANDLE hVessel, int flightmodel) : VESSEL4(hVessel, flightmodel), CustomCargo(hVessel, false) {};
	~Custom();

	ATTACHMENTHANDLE GetCargoAttachmentHandle() override;
	bool IsUnpacked() override;
	void CargoGrappled() override;
	void CargoReleased() override;
	bool PackCargo() override;
	bool UnpackCargo() override;
};
Custom Cargo Interface:
Code:
CustomCargo.h:

class __declspec(dllexport) CustomCargo
{
public:
	CustomCargo(OBJHANDLE handle, bool unpackable);

	virtual ATTACHMENTHANDLE GetCargoAttachmentHandle() = 0;

	virtual bool IsUnpacked() = 0;

	virtual void CargoGrappled() = 0;

	virtual void CargoReleased() = 0;

	virtual bool PackCargo() = 0;

	virtual bool UnpackCargo() = 0;

	virtual ~CustomCargo();

private:
	friend class UCSO_API;

	OBJHANDLE handle;
	bool unpackable;
};

================================

CustomCargo.cpp:

#include "CustomCargo.h"
#include "UCSO_API.h"

CustomCargo::CustomCargo(OBJHANDLE handle, bool unpackable)
{
	this->handle = handle;
	this->unpackable = unpackable;

	UCSO_API::AddCustomCargo(this);
}

CustomCargo::~CustomCargo() { UCSO_API::DeleteCustomCargo(this); }
Main Cargo API:
Code:
UCSO_API.h:

class UCSO_API
{
private:
	friend class CustomCargo;
	static void AddCustomCargo(CustomCargo* cargo);
	static void DeleteCustomCargo(CustomCargo* cargo);
	static std::vector<CustomCargo*> customCargoes;
};

================================

UCSO_API.cpp:

#include "UCSO_API.h"

std::vector<CustomCargo*> UCSO_API::customCargoes;

void UCSO_API::AddCustomCargo(CustomCargo* cargo)
{
	customCargoes.push_back(cargo);
}

void UCSO_API::DeleteCustomCargo(CustomCargo* cargo)
{
	std::vector<CustomCargo*>::iterator it = find(customCargoes.begin(), customCargoes.end(), cargo);
	if (it != customCargoes.end()) customCargoes.erase(it);
}
The main cargo API and custom cargo interface are all in one project and compiled into one .lib file.

The code works until the custom cargo interface calls the static add function. The problem is the method is never called actually. So the custom cargo interface constructor is executed correctly, however, the main cargo API AddCustomCargo method isn't actually called, and the custom cargo isn't added to the customCargoes vector.

I am trying to use the design @Enjo used in his HUD Drawer SDK.
Attached Thumbnails
Cargo Interface.png  

Last edited by Abdullah Radwan; 01-07-2020 at 05:17 PM.
Abdullah Radwan is offline   Reply With Quote
Old 01-07-2020, 05:52 PM   #2
Face
Beta Tester
 
Face's Avatar

Default

Quote:
Originally Posted by Abdullah Radwan View Post
 The code works until the custom cargo interface calls the static add function. The problem is the method is never called actually. So the custom cargo interface constructor is executed correctly, however, the main cargo API AddCustomCargo method isn't actually called, and the custom cargo isn't added to the customCargoes vector.
How do you know this? Stepped through debugging until the static function call, then nothing happens?

Multiple inheritance in C++ always was kind of a crutch, I wouldn't be surprised if something strange happens in the constructor chain. Perhaps "this" is returning an unexpected type, since the CustomCargo ctor is called after the VESSEL4 ctor?
Face is offline   Reply With Quote
Thanked by:
Old 01-07-2020, 05:59 PM   #3
Abdullah Radwan
Addon Developer
 
Abdullah Radwan's Avatar
Default

Quote:
Originally Posted by Face View Post
 How do you know this? Stepped through debugging until the static function call, then nothing happens?

Multiple inheritance in C++ always was kind of a crutch, I wouldn't be surprised if something strange happens in the constructor chain. Perhaps "this" is returning an unexpected type, since the CustomCargo ctor is called after the VESSEL4 ctor?
Yes. It reaches up to the custom cargo interface constructor then nothing happens. The static function isn't called, although the line is actually executed.

This way is the same way used in Enjo's HUD Drawer SDK. I don't know why it doesn't work for me.

I highly doubt "this" is returning unexpected type, since the application would crash if this happens, but it continues normally without crashing. It looks like the static function call went lost!
Abdullah Radwan is offline   Reply With Quote
Old 01-07-2020, 06:12 PM   #4
Face
Beta Tester
 
Face's Avatar

Default

Quote:
Originally Posted by Abdullah Radwan View Post
 Yes. It reaches up to the custom cargo interface constructor then nothing happens. The static function isn't called, although the line is actually executed.

This way is the same way used in Enjo's HUD Drawer SDK. I don't know why it doesn't work for me.

I highly doubt "this" is returning unexpected type, since the application would crash if this happens, but it continues normally without crashing. It looks like the static function call went lost!
Well, calls don't go lost as much as an unexpected type would crash the app, so there obviously is something strange going on.

So just to be clear: you have one DLL (the custom "vessel") which references another DLL (the custom cargo "interface" and API implementation). You followed the code from the vessel DLL to the API DLL with the debugger, and despite the call being executed with "step into" feature, you are not taken to the static function in the debugger. I can only suggest to look at the disassembly, then.
Face is offline   Reply With Quote
Old 01-07-2020, 10:51 PM   #5
jangofett287
Heat shield 'tester'
 
jangofett287's Avatar
Default

Just to sanity check, are you debugging with optimizations enabled?
jangofett287 is offline   Reply With Quote
Thanked by:
Old 01-08-2020, 04:18 AM   #6
Abdullah Radwan
Addon Developer
 
Abdullah Radwan's Avatar
Default

I think I have understood it now.

See the diagram in the attachments.

It looks like the custom cargo gets an instance of the .lib file, and the custom vessel gets its own instance.

When the custom cargo calls, the call reaches to the main API in its instance, which is different from the custom vessel main API. This way, the call never arrives at the custom vessel main API (which I want).

This answer on Stackoverflow confirms my conclusion.

How can this problem be solved? By making the library dynamic? I am not really sure as my experience in C++ in such topis is not that great.

I want to make a universal .lib file so the same file (not copies of it) is used in all custom vessels and cargoes. Is such thing possible?
Attached Thumbnails
Static library.png  
Abdullah Radwan is offline   Reply With Quote
Old 01-08-2020, 07:17 AM   #7
Face
Beta Tester
 
Face's Avatar

Default

Quote:
Originally Posted by Abdullah Radwan View Post
 I want to make a universal .lib file so the same file (not copies of it) is used in all custom vessels and cargoes. Is such thing possible?
Usually you solve this architecture with a lib and a shared DLL. In the lib, you define the classes and/or static function signatures. But instead of full implementations, you make stubs that forward the calls to a late-bound DLL. This way people can include the lib in their code and get the class hierarchy compiled into their binaries, but the actual implementation with static data-structures remains in the single DLL runtime instance.
The late-binding makes the use of the shared DLL optional, i.e. if users implement a vessel that uses your platform, it will work - though with reduced functionality - without your platform installed, like e.g. XRSound.

For this you should get yourself familiar with LoadLibrary() and GetProcAddress(), as it lays at the heart of most late-binding techniques. I'd also suggest to drop the multiple inheritance and ctor housekeeping. Instead, use the factory pattern to get a cargo object for the vessel. This would make release management a bit easier for you and your user-base.
Face is offline   Reply With Quote
Old 01-09-2020, 02:36 PM   #8
Abdullah Radwan
Addon Developer
 
Abdullah Radwan's Avatar
Default

Quote:
Originally Posted by Face View Post
 Usually you solve this architecture with a lib and a shared DLL. In the lib, you define the classes and/or static function signatures. But instead of full implementations, you make stubs that forward the calls to a late-bound DLL. This way people can include the lib in their code and get the class hierarchy compiled into their binaries, but the actual implementation with static data-structures remains in the single DLL runtime instance.
The late-binding makes the use of the shared DLL optional, i.e. if users implement a vessel that uses your platform, it will work - though with reduced functionality - without your platform installed, like e.g. XRSound.

For this you should get yourself familiar with LoadLibrary() and GetProcAddress(), as it lays at the heart of most late-binding techniques. I'd also suggest to drop the multiple inheritance and ctor housekeeping. Instead, use the factory pattern to get a cargo object for the vessel. This would make release management a bit easier for you and your user-base.
I understood your idea, but how can it be applied with DLLs? See this Microsoft page.
Quote:
Variable Scope

Variables that are declared as global in a DLL source code file are treated as global variables by the compiler and linker, but each process that loads a given DLL gets its own copy of that DLL's global variables. The scope of static variables is limited to the block in which the static variables are declared. As a result, each process has its own instance of the DLL global and static variables by default.
If every process will get its own instance of the DLL global and static variables, how can my idea be applied?
Abdullah Radwan is offline   Reply With Quote
Old 01-09-2020, 05:37 PM   #9
Face
Beta Tester
 
Face's Avatar

Default

Quote:
Originally Posted by Abdullah Radwan View Post
 If every process will get its own instance of the DLL global and static variables, how can my idea be applied?
There is only one process: orbiter.exe (or orbiter_ng.exe). Therefore, once one custom cargo DLL late-bound the common cargo DLL, there will be one set of global variables for that DLL. Later custom cargo DLLs that also late-bind the common cargo DLL will get the very same set.
Face is offline   Reply With Quote
Thanked by:
Old 01-10-2020, 05:03 AM   #10
Abdullah Radwan
Addon Developer
 
Abdullah Radwan's Avatar
Default

Quote:
Originally Posted by Face View Post
 There is only one process: orbiter.exe (or orbiter_ng.exe). Therefore, once one custom cargo DLL late-bound the common cargo DLL, there will be one set of global variables for that DLL. Later custom cargo DLLs that also late-bind the common cargo DLL will get the very same set.
Oh, I thought that each time you load the DLL a new process is created. I understand it now. So you get the same DLL for every LoadLibrary in the running Orbiter instance.

I tried it and it worked without problems Thank you so much for your help!
Abdullah Radwan is offline   Reply With Quote
Thanked by:
Old 01-10-2020, 05:21 AM   #11
Linguofreak
Orbinaut
Default

Quote:
Originally Posted by Abdullah Radwan View Post
 Oh, I thought that each time you load the DLL a new process is created.
No, that's really the difference between a DLL and an executable: An executable is loaded when a process is created and provides the main program for that process. A DLL is a file that contains helper code for other programs, and is loaded into the address space of each program that needs it. It is never launched as the initial program for a process.
Linguofreak is offline   Reply With Quote
Reply

  Orbiter-Forum > Orbiter Space Flight Simulator > Orbiter SDK


Thread Tools

Posting Rules
BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts
Forum Jump


All times are GMT. The time now is 03:35 AM.

Quick Links Need Help?


About Us | Rules & Guidelines | TOS Policy | Privacy Policy

Orbiter-Forum is hosted at Orbithangar.com
Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2020, vBulletin Solutions Inc.
Copyright 2007 - 2017, Orbiter-Forum.com. All rights reserved.