I am thinking of developing a GUI for CLIPS (since I am embedding it in another application). I have read the advanced programming and interface documentation, but a number of things are still not clear to me. I have enumerated these issues below, and hope that someone may be able to shed some light on them:
1. How to pass commands entered at the CLI (command line interface) to CLIPS
2. How to receive output from CLIPS so that it can be displayed on a window or other IO stream
3. How to call a CLIPS API function from a menu (for example)
Regarding issues 1 and 2, I suspect it is to do with CLIPS I/O routine architecture. I have read that part of the documentation but it is still not clear to me. I understand the concept of logically named routing, but when I look at the source code for existing IDEs, I dont exactly see where that is being implemented - but that again may be due to the fact that I am not familiar with the Windowing systems available so far (MAC OS X Windows, MS Windows, wxWidgets etc). In order to move away from the specifics of a particular GUI , I would like to add an extra layer of abstraction to my initial questions (1 and 2 above)
Question 1 (from above) Redux:
If I have a (C++) string variable named cmd which contains the text entered at the command line by a user, how do I send this command to CLIPS. i.e. how may I implement this function:
//return false on error, true otherwise
bool sendCLICommandToClips(const std::string& cmd)
// how do I send data held in the variable 'cmd' to CLIPS?
Question 2 (from above) Redux:
If CLIPS is sending output data (destined for say a window or printer), how can I "intercept" this data ?
void handleCLIPSOutputCallback(void * some_data /*not sure what*/)
/* how do I determine where it is destined for - i.e. */
// 1. is it for a window (if yes, which of the available windows?)
// 2. is it file I/O ?
// 3. is it database I/O ?
// 4. is it for printer ?
// 5. is it for some other custom handler ?
Please feel free to suggest new prototypes (signatures) for the above functions. A further point worth making is that actual code may not be necesssary to explain ow to implement the functions above (although that would be very nice and very much appreciated).
Regarding question number 3 I asked above, my understanding is this (once again, I have written it without any particular windowing system in mind, in order to understand to avoid conflating specific windowing system peculiarities with the issue at hand - using menu item selection to send appropriate instructions via to CLIPS via the CLIPS API :
// Menu item handler
void myMenuHandler(const MenuItemType mtype, const MenuData& mdata)
case BLOAD_CLIPS_FILE: // as an example
// extract data required for CLIPS API function arguments
const char* fname = mdata.fname.c_str();
// call appropriate CLIPS API function (Bload in this case)
Is my understanding correct, or have I misunderstood something important (or are there some "gotchas" that I need to be aware of?).
There are different ways that you can wrap a development GUI around CLIPS, but they revolve around an internal command string maintained by CLIPS. The non-GUI command line version of CLIPS invokes the CommandLoop function which loops calling an event function that stuff characters into the command string(using ExpandCommandString). When a complete command is found, it is executed, the command string is cleared, and polling of the event function continues to find the next command. I/O is handled by predefined routers for standard input and output (for which there are a number of predefined logical names listed in the router documentation) and these basically map to getc and printf.
The Windows GUI invokes the CommandLoop function, but overrides the default event function (using SetEventFunction) and defines new routers for standard input and output that override the default routers by use of a higher priority. The overriding event function calls the Windows specific GetMessage function and handles the returned event (which could be a key press, menu command, etc). In the event of a key press, it appends the decoded key using AppendCommandString. When a complete command is eventually formed, this will be executed by CLIPS. I/O other than that for the command prompt is handled by the overriding router. Instead of getc, reading from standard input calls the Windows specific GetMessage function specifically looking for key strokes. Instead of printf, printing to standard output is handled by the code for the GUI display window.
The Mac OS X GUI is somewhat similar, but it does not invoke the CommandLoop function. This environment allows you to have several environments open in separate windows, so it did not make sense in this case to have each one invoking the event function in the GUI wrapper. Instead, the GUI handles the event loop and routes the keyboards events to the appropriate environment using AppendCommandString. The GUI determines when a complete command has been formed (using the CommandCompleteAndNotEmpty function) and invokes execution of the command. Each environment still overrides the default I/O router for standard input/output to use GUI specific code for the display windows.
So, for question #1, you typically do not set the command string all at once (although in some cases you might if you implement a menu command by constructing a command string such as for loading a CLIPS file).
For question #2, you typically only intercept known logical names, so these would either be the predefined system names (stdin, stdout, wprompt, wdialog, wdisplay, werror, wwarning, and wtrace), or names maintained within your code. For example, the CLIPS I/O functions (read, printout, format, readline…) only look for the predefined system names or those logical name associations created using the open command. If you want to allow the user to dynamically create a window that you can send output to from the printout command, you'd associated a logical name with it when it was created and your code would use that name to determine which window should receive I/O.
For question #3, your overriding event function would dispatch menu commands, so the example you have is the correct way to do it.
Thanks for the prompt response. Much appreciated. There is a lot of information in your response, and I have to admit that I dont understand all of what you wrote.
I shall have to go back to read the documentation (especially the part on routing, possibly revist the source code again and then attempt to build the GUI. If I are encounter any problems, or things still; remain unclear, I shall ask for your further guidance/assistance.
Once again, thanks for your help.
Another quick question. By my understanding so far, the current (default) routings are mapped to stdio and stdin.
If this is the case, then this would be the steps to create an embedded application:
1. Create the CLIPS shared library (libclips.so.6.3.0 in my case)
2. write a simple CONSOLE application that embeds the CLIPS rule engine (using one of the examples in the advanced programming manual), and link the console app to the shared library built in step 1 above.
If my understanding is correct, this would be the way to build a CONSOLE app that embeds CLIPS and use stdin/stdout for i/o.
Is my understanding correct?. If this is not the case (or I have missed something), please clarify.
I think succesfully building a CLI application that embeds CLIPS is an essential first step on the way to writing a custom GUI around CLIPS.
I look forward to your response.
Your understanding is correct.
Thanks for the clarification.