Sophie

Sophie

distrib > Fedora > 13 > x86_64 > media > updates > by-pkgid > fba45f71a752b715a482b8d4a068e673 > files > 278

simspark-devel-0.2.2-3.fc13.x86_64.rpm

<html>
<head>
<title>How to expose C++ functions to Ruby Scripts</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<link rel="stylesheet" href="highlight.css" type="text/css">
</head>

<body bgcolor="#FFFFFF" text="#000000">
<p><font size="6"><i><b>How to expose C++ functions to Ruby Scripts</b></i></font></p>
<p align="left"><font size="4">As we want to expose much functionality to the 
  script side, we also want to be able to call C++ functions from Ruby. In order 
  to do this, we intercept unknown function calls on the Ruby side. The parameters 
  are converted on the C++ side. There we know the name of the function to call, 
  the object to call them on, and the parameters. But, how do we actually call 
  the correct C++ function. The answer lies in the class object. The class object 
  will contain the necessary meta-data to reroute the function call to the correct 
  C++ function. The class object defines the interface of the class to the script 
  side. This is done in the same file as the class definition was performed (the 
  additional CPP file). Let's go back to our Simple class from earlier HowTos:</font></p>
<pre><span class="dir">#include &lt;zeitgeist/leaf.h&gt;
</span>
<span class="key">class </span>Simple : <span class="key">public </span>zeitgeist::Leaf
{
<span class="key">public</span>:
  Simple();
  <span class="key">virtual </span>~Simple();
  
  <span class="typ">void </span>DoSomething();
  <span class="typ">void </span>PrintString(<span class="typ">const </span>std::string&amp; s);
  <span class="typ">void </span>PrintInt(<span class="typ">int </span>i);
  <span class="typ">void </span>PrintFloat(<span class="typ">float </span>f);
  <span class="typ">void </span>PrintBool(<span class="typ">bool </span>b);
};

DECLARE_CLASS(Simple);
</pre>
<p><font size="4">Now, let's say we want to expose the DoSomething() method. simple_c.cpp 
  would look like this:</font></p>
<pre><span class="dir">#include </span><span class="dstr">&quot;simple.h&quot;</span><span class="dir"></span>

<span class="key">using namespace </span>zeitgeist;

FUNCTION(doSomething)
{
  <span class="key">if </span>(in.size() == <span class="num">0</span>)
  {
    Simple *simple = <span class="key">static_cast</span>&lt;Simple*&gt;(obj);
    simple-&gt;DoSomething();
  }
}


<span class="typ">void </span>CLASS(Simple)::DefineClass()
{
  DEFINE_BASECLASS(zeitgeist/Leaf);
  DEFINE_FUNCTION(doSomething);
}
</pre>
<p align="left"><font size="4">Yes, you guessed right ... more helper macros. 
  Every function is declared using the FUNCTION()-macro. As a parameter it takes 
  the name of the function. I like to always have Ruby-side functions start with 
  a lower-case letter and C++-side functions with a capital letter. The function 
  macro just declares a function, which takes two parameters: obj and in. obj 
  is the object we are calling the function on (basically, the 'this' or 'self' 
  pointer). in is an STL vector of input parameters. The vector holds boost::any 
  types, which have to be cast back. Within the function, we check if the number 
  of parameters match (in our case 0 parameters). We cast the obj pointer to the 
  correct type and call the appropriate function. In DefineClass() we also have 
  to define the function using the DEFINE_FUNCTION() macro. After this has been 
  done (and the class object is registered with the Core), we can execute the 
  following script-code:</font></p>
<p align="left"><font size="4">mySimpleObj = new ('Simple', 'test');</font></p>
<p align="left"><font size="4">mySimpleObj.doSomething();</font></p>
<p align="left"><font size="4">This would then call the C++ member function DoSomething(). 
  Now, how about passing some parameters. Let's marshall the PrintInt() function:</font></p>
<pre><span class="dir">#include </span><span class="dstr">&quot;simple.h&quot;</span><span class="dir"></span>

<span class="key">using namespace </span>zeitgeist;
<span class="key">using namespace </span>boost;

FUNCTION(printInt)
{
  <span class="key">if </span>(in.size() == 1)
  {
    Simple *simple = <span class="key">static_cast</span>&lt;Simple*&gt;(obj);
    simple-&gt;PrintInt(any_cast&lt;int&gt;(in[0]));
  }
}</pre>
<p align="left"><font size="4">The above function obviously also would need to 
  be defined in DefineClass(). Now, we see that any_cast is used to retrieve the 
  type (!) of the parameter stored in the vector 'in' at position 0 (the first 
  parameter from left to right maps to position 0). It should be noted, that this 
  way of marshalling is not safe, meaning that exceptions will occur if a wrong 
  parameter has been issued script-side. So, please be careful when doing this 
  ... and take care to pass correct parameters on the script side. The problem 
  is, that Ruby is dynamically typed, whereas C++ is statically typed. Also, no 
  types are coerced automatically. So, if you cast to an int and somebody uses 
  a float in the script, this will likely cause an error!</font></p>
<p align="left"><font size="4">Another note is that in the current implementation 
  NO return parameters can be passed to the script side. This is unfortunate, 
  but would have created yet another (major) source of typing errors. One is dangerous 
  enough. Also, scripts should be kept linear to make them easier to read.</font></p>
<pre>&nbsp;
</pre>
</body>
</html>