Introduction
Hello. I am currently developing a software involving
- A GUI in C# / vb.net
- A computation kernel in julia
To handle to communication business I am using Json.net for the C# part and the BSON package in julia. The communication itself is performed over named Pipes as everything runs on the same local machine.
In my julia program, I have developed
- a scientific package (let’s call it
SCI
) that handles the whole computation–heavy stuff, that can also be run autonomously - a server package (
COM
) that handles all the communication stuff including (de-)serialization, argument checking and so on. Once started, this server waits for instruction over the pipe, and calls some kind ofcompute
command, or assigns values to variables when required.
Each upper-level function of SCI
takes one or multiple arguments and returns only one.
Each of these functions has a specific return argument type (which is hence only used once).
Currently the process is the following:
- A message is received
- This message is converted to a
struct
based on types defined inSCI
or anInstruction
type defined inCOM
. - Two cases :
- If the object retrieved is a
SCI
-definedstruct
:- It is checked against a list of authorized types defined in
COM
based on knowledge ofSCI
- it is stored in the field of a
mutable struct
(Container
) that is currently defined inCOM
- It is checked against a list of authorized types defined in
- If the object is an instruction :
- It is checked against a list of authorized instructions known from
SCI
- It is passed as an argument to a
compute
function that call function fromSCI
and store the result inContainer
- Special cases :
- The instruction is
exit
, the server stops - The instruction is
return_X
, the server returns the value of variableX
- The instruction is
- It is checked against a list of authorized instructions known from
- If the object retrieved is a
The authorized types and authorized instructions are obtained from SCI thanks to the following bit of code in COM
using SCI
const auth_funs = filter(n -> eval(getfield(SCI, n)) isa Function, names(SCI))
const auth_types = filter(n -> getfield(SCI, n) isa Type, names(SCI))
The container type itself is defined manually.
My real question starts here
Obviously, if I were to only use these two packages, this state of development would be totally enough.
However, the COM
package feels too much tied to the SCI
package.
What if I wanted to use the same server, with another SCI2
package ?
What COM
really requires is
- To be able to load some package
- To detect some properties of this package
- To ensure that this package defines a
compute
function taking the appropriate arguments, and potentially aContainer
type
A very naive idea would then be to call something like using COM(SCI)
, however this is not possible.
So my question (finally) is : How to handle such dependency of one package versus another, dependency that is known at compile time (I believe,correct me if I’m wrong), but that would depend on some parameter defined in a const
for instance, by the user.
The goal would be to make COM
as much agnostic as possible to SCI
so that COM
does not depend on it. This point is kind of another question: if COM
loads SCI
, shouldn’t SCI
be add
ed to the environment of COM
? (which it is in my current implementation).