[ENH] Partial support for C++ code generation in CCodeGenerator
Robert M. Fuhrer
rfuhrer at watson.ibm.com
Thu Apr 20 19:59:49 UTC 2000
Here's a very small file-in that gives C++-generation capability to the
CCodeGenerator and friends.
I wrote it while developing a plug-in that interfaces to a C++ DLL.
How does it differ from the ordinary mode? Mostly just two things:
1) Places generated code in a file with a ".cpp" suffix.
2) Puts "extern "C"" in the right places so that the plug-in's exported
routines have the names that the Squeak plug-in mechanism expects them to.
To create a C++ plug-in, just do the usual thing, adding the trivial class
method "isCPP" to your plug-in class (the one that actually gets translated
into C/C++). In case it's of use to anyone, I appended below my partial
recipe for writing Squeak plug-ins using the "named primitive" mechanism.
What I found on the Web a month or two ago didn't have enough detail for my
purposes.
The file-in is a delta on the Squeak 2.7 source base. Hope that's still
useful (I haven't had time to read the mailing list in weeks, so for all I
know everyone's at 3.0 already :-)).
I invite comments/criticisms on the file-in or the half-recipe below (or
contributions to make the recipe more accurate/complete). If it's useful, I
might even put the recipe and other stuff on my Web page (or on the Swiki).
BTW, although the CCodeGenerator mechanism is way cool, I found that I
ended up writing code fragments like the following, much of which looks
anything but intuitive:
primitiveDoStuff
| a b c |
self export: true.
self var: #a declareC: 'int a'.
self var: #b declareC: 'int b'.
self var: #c declareC: 'int c'.
myGlobalVar = nil ifTrue: [^ interpreterProxy primitiveFail].
a _ interpreterProxy stackIntegerValue: 2.
b _ interpreterProxy stackIntegerValue: 1.
c _ interpreterProxy stackIntegerValue: 0.
interpreterProxy failed ifTrue: [^ nil].
self cCode: 'myGlobalVar->SomeMethod(a, b, c)'.
interpreterProxy pop: 4
"rcvr + args"
So, two questions:
1) Am I doing things oddly?
2) If not, is anyone working on a mechanism to generate C/C++ code from
more natural-looking (and safer) Smalltalk code?
=============================================================
Recipe for Plug-in Primitives
=============================================================
On the Squeak side:
1) Create a "plug-in class" that will house the Smalltalk code from which
the C/C++ primitive code will be generated. Let's say the class name is
"FooPlugin".
2) Plug-in class vars/methods:
2a) Define the module name by overriding the method 'moduleName':
FooPlugin class>>moduleName
^ 'SqFoo'
2b) If this is to be a C++ plugin, override the class method isCPP to
return true:
FooPlugin class>>isCPP
^ true
This makes the generated file name end in ".cpp", and triggers a
few other changes in the generated code (like putting an "extern "C""
declaration around the routine prototypes).
2c) For each static global variable that you need to define in the
generated C/C++ code, add an instance variable to the class definition.
2d) Override the declareCVarsIn: method in your plug-in class to
provide C declarations for each static global variable. E.g., add code like
the following:
cg var: 'myGlobal' declareC: 'static int *myGlobal = 0'
2e) If the plug-in DLL needs any header files to compile, add code
like the following to declareCVarsIn:
cg addHeaderFile: '"myheaderfile.h"'
3) Plug-in instance vars/methods:
3a) For each DLL routine/method you want to expose to Squeak, write a
corresponding routine that will generate the bridging code. I.e., write
something like the following:
primitiveMonoPressure
| pressure channel |
self export: true.
self var: #pressure declareC: 'int pressure'.
self var: #channel declareC: 'int channel'.
yumi = nil ifTrue: [^ interpreterProxy primitiveFail].
pressure := interpreterProxy stackIntegerValue: 1.
channel := interpreterProxy stackIntegerValue: 0.
interpreterProxy failed ifTrue: [^ nil].
self cCode: 'yumi->TMonoPressure(0, channel, pressure, yumiPort)'.
interpreterProxy pop: 3 "rcvr + args"
Notes:
- The statement "self export: true" is a signal to the code
generator that this routine should be exported. Otherwise, the routine
will not be visible to Squeak. You might not want the routine exposed if
it's an implementation aid.
- The statement "self var: #pressure declareC: 'int pressure'" declares
a local variable in the C/C++ code that is also a local var to this Squeak
method. This allows you to write more-or-less (accent on "less") normal
Smalltalk code to manipulate the variable, and have corresponding code
generated for C/C++. Technically, such a declaration isn't necessary, but
it helps you write more normal-looking Squeak code that gets translated in
mostly-reasonable ways to C/C++.
- Note that certain control constructs such as ifTrue:ifFalse:,
whileTrue:, and so on, are translated to C/C++ by the Squeak code
generator, so you can use them to do non-trivial work in the bridging code.
[In fact, this also allows you to write plug-in DLL's that aren't bridges
at all, but simply compiled to native object code for performance reasons!]
- The statement "self cCode: 'yumi->TMonoPressure(0, channel,
pressure, yumiPort)'" is an example of how to pass C/C++ code fragments
verbatim into the generated C/C++ side.
- The statement "interpreterProxy pop: 3" is for stack
clean-up. It seems that the callee (the Squeak code you write) must do
this manually :-(. The rule seems to be that one must pop N+1 stack items
if the primitive takes N args.
- There is no explicit declaration of how many parameters this
primitive will take. Calls to obtain certain stack items (i.e. parameters)
are type-specific, and look something like "pressure := interpreterProxy
stackIntegerValue: 1" or "pressure := interpreterProxy stackObject:
1". The index passed to such a stack operation for the i'th [1-based]
primitive parameter is N-i. E.g., the first of 2 parameters is accessed
via "interpreterProxy stackObject: 1".
4) Create a "plug-in interface class" that will wrap calls to the C++
primitives. Let's say it's called "FooInterface", corresponding to
"FooPlugin" above.
4a) Write the "plug-in interface" instance methods:
For each primitive method in the plug-in, write a method of the form:
FooInterface>>doStuff: value withArg: arg
<primitive: 'primitiveDoStuffWithArg' module:'sqFoo'>
^ self
Take care to make sure that the number and order of the args
matches the referencing code in your primitive implementation. N.B.: Pay
attention to the above note that talks about parameters.
[Here's a safety issue that's waiting to bite people!!! Great
opportunity to make it easier to write plug-ins by making sure that one
can't get stack/arg referencing wrong.]
On the C/C++ side (basically, you just need to get the code to compile; the
description below assumes you're doing so under Win32):
1) Create a normal empty Win32 DLL project (you can use the VC++ wizard).
2) Add the Squeak-generated plugin C/C++ source file to the project.
3) Get or generate the relevant header files
3a) Call InterpreterSupportCode>>writeSupportFiles, or grab the
relevant VM source bundle from somewhere. For Win32, I couldn't find all of
the necessary header files (specifically sqWin32.h and sqWin32Alloc.h)
anywhere else, nor could I find a way to generate them, so I pulled from
the VM source archive.
3b) I found it necessary to tweak sqPlatformSpecific.h so that it
didn't attempt to (improperly) re-define GetTickCount(). [There was a
mismatch between the definition there and the canonical one in the Win32
API header files from Microsoft Visual C++ v.6, which caused a compilation
error.]
4) Add the directories containing the VM's header files to your
compiler's C++ include path.
5) Make any tweaks to the project file that you need to get your code to
compile (e.g. additional include/link paths).
6) Add a post-build step to copy the DLL to E:\Squeak. [Not really
necessary, but makes one's life easier.]
7) Compile the project. If you didn't add the post-build step, manually
copy the DLL to wherever your Squeak VM lives.
8) Run Squeak, and watch the fur fly!
-------------- next part --------------
A non-text attachment was scrubbed...
Name: Plugin Enhancements.1.cs
Type: application/octet-stream
Size: 2688 bytes
Desc: not available
Url : http://lists.squeakfoundation.org/pipermail/squeak-dev/attachments/20000420/b29226d0/PluginEnhancements.1.obj
More information about the Squeak-dev
mailing list
|