When you need to provide your simulation as an external library (dll, lib) to someone, you should simplify the API as much as possible so that you are able to provide the minimal amount of header files, without exposing details the user doesn't care about. You can achieve this by hiding all the internal dependencies in the implementation (cpp) file. If the user has to be able to create multiple (concurrent) simulations, you can use a static map to hold each simulation object. Here is an example (C++11):
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Blog post: https://simulinkforsoftwareengineers.blogspot.com/2021/09/hiding-simulation-details-in-c-library.html | |
#include "SimAPI.h" | |
#include<map> | |
using namespace std; | |
#include<SimState.h> //this is internal to simulation, which the library user does not need to know | |
#include<Solver.h> //this is internal to simulation | |
class Simulation { | |
public: | |
State state; //State is defined in SimState.h | |
Solver solver; //Solver is defined in Solver.h | |
}; | |
Simulation* sim; | |
static map<int, Simulation*> sims; | |
SimAPI::SimAPI(int simKey) { | |
if (sims.count(simKey) > 0) throw SimKeyAlreadyExistsException(simKey); | |
sim = new Simulation(); | |
sims.insert({ simKey, sim }); | |
} | |
SimAPI::~SimAPI() {} //Note: The desctructor is called right after constructor when this class is used like SimAPI s = SimAPI(...) due to a temp object being created. It is not the case for SimAPI* s = new SimAPI(...). | |
Outputs SimAPI::runOneStep(int simKey, int timeStep_s) { | |
Simulation* pSim = sims[simKey]; | |
State currentState = pSim->state; | |
State newState = pSim->solver(currentState, timeStep_s); | |
pSim->state = newState; //update simulation state | |
Outputs out = Outputs(); | |
out.time_s = newState.time_s; | |
out.hMSL_m = newState.hMSL_m; | |
return out; | |
} | |
void SimAPI::terminate(int simKey) { | |
sims.erase(simKey); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include<exception> | |
#include<stdio.h> | |
class SimKeyAlreadyExistsException : public std::exception { | |
public: | |
SimKeyAlreadyExistsException(int simKey) : existingKey(simKey) {} | |
const char* what() const throw() { | |
char message[200]; | |
sprintf_s(message, "simKey (%d) already exists, please enter a unique simKey!\n", existingKey); | |
return message; | |
} | |
int getExistingKey() { | |
return existingKey; | |
} | |
private: | |
int existingKey; | |
}; | |
class Outputs { | |
public: | |
int time_s; | |
double hMSL_m; | |
}; | |
class SimAPI { | |
public: | |
SimAPI(int simIndex); | |
~SimAPI(); | |
Outputs runOneStep(int simIndex, int timeStep_s); | |
void terminate(int simIndex); | |
}; |
No comments:
Post a Comment