Sunday, September 12, 2021

Hiding internal details of a C++ library

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):

//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);
}
view raw simAPI.cpp hosted with ❤ by GitHub
#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);
};
view raw simAPI.h hosted with ❤ by GitHub

No comments:

Post a Comment