I am writing a rules engine for an AI module. The module will store and retrieve rule sets from a database. The intention is for the module to edit/enhance/correct rule sets which will then be executed by the engine. The engine would launch a thread which in turn would create a cling interpreter instance to execute the rule set. In order for it to work correctly, the C++ module needs to pass a context object instance to the cling script and the script needs to return a result object back to the C++ module.
As I said, the cling script (a c++ object and associated methods constituting the rule engine) would receive the rule set and the context object, execute the rules, and then return a result object back to the compiled caller.
Can this be done? I’m looking for the proper set and sequence of API calls to accomplish this objective.
Cling sounds like a perfect match here, this is exactly what it was designed to do.
Code is passed to cling as a string. You can use Interpreter::evaluate to receive the object back as a Value, or even simpler, you use the same approach you could use for passing values into cling: passing the address of a variable, that the code run inside cling will access, eg using Interpreter:: process. Example code snippet:
int out = 42;
int in = 17;
stringstream sscode;
sscode << "*((int*)" << &out << ") = *((int*)" << &in << ") * 2";
interp.process(sscode.str());
Now, out is 34.
We’ll be happy to provide more detailed answers should you have specific questions!
As it happens, I have run into what I believe are documentation obstacles. By that I mean that I cannot find something similar to we used to call a programmers guide. This was a document that laid out both the concepts of the language,utility or whatever it was, and the gross implementation details.
If there were such a beast for ‘Cling’, I would expect to be able to find a section called something like ‘Embedding Cling into your program’. In that section would be found a simple explanation of what header files to use, the principal functions to be called to instantiate the interpreter, and what libraries to link with for static or dynamic linking of the user’s program.
I hate wasting the time of busy gurus but I would greatly appreciate it if you could point me to a source for answering the following questions for my intended use cases:
What header files do I need to include in my C++ compiled program to access the Cling interpreter’s needed data structures?
What libraries and in what order will I need to link the Cling interpreter to my C++ compiled program?
My C++ program will make extensive use of the Posix thread facilities. I intend to manage a pool of worker threads that will each independently utilize the Cling interpreter to run C++ scripts. The worker thread pools will be managed by schedulers whose services are enlisted by creating requests that are appended to the schedulers request queues. To facilitate inter-thread communications, the interpreter threads will lock a schedulers mutex, add to their request queue, and then wakeup the scheduler thread my signaling with its condition variable. Therefore, the interpreter scripts will need:
3.1 Interpreter needs to receive a pointer to a mutex belonging to the C++ code.
3.2 Interpreter needs to be able to load ‘libpthread.so’ to enable the calling of pthread_mutex_init, pthread_mutex_trylock, pthread_mutex_lock and pthread_mutex_unlock
3.3 Interpreter needs to receive a pointer to a condition variable belonging to the C++ code.
3.4 Interpreter needs to be able to call pthread_cond_init, pthread_cond_wait, pthread_cond_signal with the passed pointer to the condition variable.
Finally, it would be very nice if I were able to reset the interpreter instance back to its initial state after successfully completing the primary script so I did not have to fully construct a new instance of the interpreter before returning the work thread back to the available list in the worker thread pool.
Would a sample program suffice? We had another request for a CMakeFile.txt that shows how to build a project using cling as a library; this could take care of both.
Would that be an acceptable solution?
The problems I have with documents is that they are even more annoying to write than code and they are more ambiguous to read than code and they are less maintainable (because no compiler ever complains).
I’m always happy to have a good example. I agree with your comments about the fact that writing documentation tends to be an annoyance. I have always tried to get management to budget for a good technical writer to assist with these burdensome tasks. Still, I know it can still be painful to have to slow down and explain things to the tech writer so he/she can write meaningful prose that embraces the meaning and elegance of the artworks that have been produced.
Look at cling/tools/driver/cling.cpp; it shows what a binary using cling can look like: how to create an Interpreter, which headers to #include.
Its CMakeLists.txt shows what to link against. But most of the clang and llvm libraries are pulled in as dependencies of libclingInterpreter, libclingUtils etc - depending on what you use (MetaProcessor? Or just the Interpreter?) you might need different libraries.
Have a look at cling/lib/Interpreter/CMakeLists.txt which enumerates these libraries. It’s a bit involved, because this uses the llvm machinery for translating functionality (“native”) into library names. If you use CMake you should be able to use the same approach given their FindLLVM or whatever it’s called. I’ve never done this myself - so it’s not a question of me not sharing knowledge but of me now knowing If you have a CMake “recipe” I’d appreciate if you can share it.
Regarding the second part: you can link your binary against libpthread and cling will be able to resolve symbols (specially if you build your binary as -rdynamic, on Linux). You can then simply interpret #include <pthread.h> and use its functionality in the interpreter.
You can communicate with the interpreter world either through cling::Value or through the address passing I explained above.
I’ve been messing with cling/tools/driver/cling.cpp like you suggested. I’ve been programming since the 70’s and I’ve used make since the late 80’s. However, I’ve never been real attracted to cmake. I guess I should probably see if O’Reilly has a cmake book, but at the moment the CMakeLists.txt file does not enlighten me with regards to what libraries I should use to embed cling in my program.
Even after running cmake for that part of the source tree, I can’t seem to end up with a Makefile that has a clearly defined $(LIBS) variable much less an actual link to an executable statement. Come on. Someone has to have done this.
Ok, I found an old posting by Axel that helped lead the way. There’s a thread that is called:
How to properly link cling into my executable with cmake
The first post is Oct '15, the last one is Sep '16. The last post says that there is now a “libcling.so”. So the solution to compiling and linking the program cling/tools/driver/cling.cpp is as follows for me based upon my copying the cling.cpp program to my root build directory (i.e. /src/root-6.08.06/build - the directory where I ran cmake …) and having run “make install” after the 7 day creation exercise (well not really 7 days, but lengthy). Therefore the following was required to build it:
Aparently, most everything was stuffed into the libcling.so library. The library libclingUserInterface.a was necessary because the source file ‘cling.cpp’ instantiates UserInterface (cling::UserInterface ui(interp) to accommodate interactive code entry/execution. I don’t believe I’ll need that for my program but it’s nice to have it available for some other purposes.
If anyone has any questions about this please feel free to ask: artsera@yahoo.com
Indeed, that’s the easiest approach. If you want to avoid the runtime mess of finding the shared library then take the static libraries (.a files), else libcling.so is the way to go.
Because trying to compile cling exhausted my disk space, I only installed the binaries, hence there is no ‘/cling-source-dir/tools/cling/tools/demo’ directory on my machine… What should I do instead to compile this on ubuntu 18.04 ?
and get:
– Configuring done
CMake Error: CMake can not determine linker language for target: cling-demo
CMake Error: Cannot determine link language for target “cling-demo”.
– Generating done
CMake Warning:
Manually-specified variables were not used by the project:
Works great .
I am curious if it’s possible to do the same with C++ Classes.
Specifically instantiate a native class from the interpreter side and call some of its methods and vice versa .
I am aware of the C++ name mangling issues .
I am aware that it’s possible to create some extern “C” wrapper functions that will do the job , but I consider it to be an workaround .
Is there another way ?
Sure, that’s the whole beauty of cling: just #include a header in cling, and use the same header in your compiled code, and if the flags are all lined up then you can pass objects from compiled code into the interpreter and back! That’s what ROOT does, in production, 24/7, on 100’000s cores world-wise: it works!