Hi,
i tried to write a testplugin which simply returns a double. If I follow the the example I have to to set the data type of the return structure to DOUBLE. But there is no such type in contrast to INTGR !
So for me it is not clear if it is ok simply to use
DLLEXPORT double fktname(...){..}
or if I have to use Type CMPLX with cmplx_val.imag set to zero.
It would be helpful if this would be clearly specified at some place e.g. in the file README.
By the way what is the
void *p
from
DLLEXPORT struct value Q(int nargs, struct value *arg, void *p)
or
DLLEXPORT struct value sinc(int nargs, struct value *arg, void *p)
DLLEXPORT struct value nsinc(int nargs, struct value *arg, void *p)
(uigamma_plugin.c, demo_plugin.c)
It is never used.
BR
NoZa
Gnuplot has no type "double" or "float". The variable types are
Real numbers are stored as type CMPLX with the imaginary compent equal to zero.
You are quite right that the plugin mechanism deserves a full writeup somewhere. I'll try to give a brief description here.
The additional parameter
(void *)pexists so that the plugin can create and manipulate a persistent data structure or other object that is otherwise not visible to gnuplot.The plugin's initialization routine can return an opaque value in
pthat gnuplot stores as part of the function definition. When the plugin function is later called, gnuplot passespback to it in parallel with the regular parameters. Gnuplot does not know what the plugin will use it for.A simple example would be if the initialization routine creates an array and fills it in for later use, either computationally or by reading data from a file. When the function is later called, it can access the stored data via the copy of
pthat is passed in as part of the call.A non-trivial example would be if the initialization routine opens a file or pipe from an external process and passes a file descriptor back in
p. Later invocation of the plugin function could then read data from that file descriptor.The reason for doing it this way rather than simply making the data structure or file descriptor or whatever a static memory area in the plugin source is that the plugin is a shared object. This way the plugin can be loaded multiple times in parallel, creating a new data structure or file descriptor each time rather than clobbering the previous one.
When gnuplot exits or releases the plugin function the plugin's destructor function is called, again passing the original
pback so that the memory can be released or the file descriptor closed or whatever else is needed for cleanup.Hi Ethan,
thanks for your help so far!
But this answer raises new questions.
You are mentioning an init and an destructor function. I assume this are
(From gnuplot_plugin.h )
According to that a plugin has to define a function named gnuplot_init which got an function pointer to an plugin function.
I assume if the plugin provides more than one "working" fuction, this gnuplot_init is called for each of this functions. Correct?
I also assume it has to be ensured that if no struct/object is neccessary NULL is returned and this happens at the time of importing the function.
Another implication for me is that a plugin uses more than one struct/object for every of this the function gnuplot_fini is called for each of them.
Please correct me if my assumtions are not correct.
Please allow me one additonal remark on this architecture. It offers a great deal of flexibility for plugins and their possible applications.
With regards to this, is it possible to bind this plugin functions to keys?
BR
NoZa
Your questions got me to look more deeply into the state of the plugin code and then try to put together a demo that illustrates the use of the initializer / imported function / destructor design. Along the way I discovered that the destructor end of it had never been completely implemented, so I had to tackle that first. I still intend to write out proper documentation but for now I hope the code itself and the accompanying comments will suffice.
The new coding example starts at line 93 of the file .../demo/plugin/demoplugin.c and the demo that calls into it is .../demo/plugin/plugin2.dem.
As to your specific questions:
* Yes, if there is no function gnuplot_init in the shared object then a NULL pointer is returned
* The initializer and destuctor functions both take as an input parameter a pointer to the function they belong to. This was the part of the destructor API that was incomplete and I had to fix. The initializer is called every time the gnuplot
import funcnamecommand is executed. Gnuplot first obtains the address of funcname and then passes it to the initializer as a parameter. So the initializer can tailor its action to the function being initialized, or just return NULL if the function needs no initialization. Similarly the destructor is passed the address of the imported function being released, and can tailor its cleanup actions to match whatever the initializer did for that function.* Once a function has been imported into gnuplot it can be used just as any of the pre-defined or user-defined functions. This includes invoking from the action bound to a hot key active while mousing. Example below:
Any feedback would be great, particularly if you can suggest a more portable example of a useful but possibly system-dependent function that would logically be implemented as a plugin rather than added to gnuplot proper. Something like, I don't know, a program that monitors a home weather station and formats the data for return to gnuplot when queried.
cheers,
Ethan
I should have added links to the relevant new code and sample plugins:
Revised destructor API
https://sourceforge.net/p/gnuplot/gnuplot-main/ci/850d0b6fe0940ec3b515a048735b96d66f4b34f0/
New plugin example code
https://sourceforge.net/p/gnuplot/gnuplot-main/ci/9cb52321e3a0f2d72652e1d8db1e3953db79a7a0/
https://sourceforge.net/p/gnuplot/gnuplot-main/ci/master/tree/demo/plugin/plugin2.dem
Hi Ethan,
thanks for the detailed reply!
If you don't mind, I can supply this documentation , or at least the rough draft version of it.
Because as someone who is just starting with this feature, my perspective may be more in line with that of other users who are in the same situation.
Another question regarding the shared object pointer.
As far as I understand it, Gnuplot only stores the return value and presumably only uses it to determine whether the destructor needs to be called. So it should be save to return any value from the initializer if no memory was allocated. Or there are other reasons to take into account?
You are also using the term "token" in this context. This can be interpreted to mean that it is not necessarily a genuine pointer to a part of the memory. So it could also be an index of a table of any kind.
Regarding the usefull plugin function, my idea was more or less identical to your personal weather station. I was thinking to produce kind of continues real time plot which only shifts the new data in and the old data out instead reload all data. Including setting the time range accordingly.
Because of this, would it be possible to call some functions inside GP e.g. set xrange , the time functions or fit etc. from the plugin. Is there a kind of API which is used this internal functions?
Another idea goes in the direction of handling complex binary or textual fileformats. e.g. SCADA
Have a good time
NoZa
I will try to describe this more clearly.
The value returned by the intializer is an opaque token. Gnuplot makes no assumptions about whether it is a pointer or an index or any particular data type. The token is stored as part of the function definition in gnuplot but is not visible to the user. Whenever gnuplot calls the imported function itself, or eventually the destructor function for it if there is one, it passes the token back in as an extra parameter. Again gnuplot makes no assumptions about what it will be used for, but logically it would be used to retrieve and/or free any memory-resident data created by the initializer.
The destructor gnuplot_fini is always called if it exists. It is passed both the address to the function being released and the opaque token created for that function when it was imported. The destructor can inspect both of these and decide if anything needs to be done.
A gnuplot user variable consists of a name, a value, and some associated bookkeeping. The value is held in a structure whose C language API is exported in gnuplot_plugin.h. Any number of gnuplot variables can be passed by value into the imported function, and the function can return any single gnuplot value. As shown in the demo plugin2, the return value can be an array, so in practice the imported function can return more than one piece of information.
As of now there is no way that an external function can call back into gnuplot-internal functions. However, you could certainly return an array of range values, or an array of initial settings for a "fit" command, so long as the gnuplot script is prepared to deal with the result. Back-of-the-envelope example:
I think it should be straightforward to write plugins in other languages, e.g. rust since it seems very trendy. This would involve translating the information in gnuplot_plugin.h into the appropriate idiom for that language. I don't think it would require any changes to gnuplot proper, but if I'm wrong I'd be happy to look at proposed changes to allow it.