Saturday, April 9, 2011

Scripting C++/cli with IronPython (Visual Studio 2008 & IronPython 2.6.1)

Even though there has been a lot of talk about IronPython, there has been very little info about how to use it with C++/cli.  I’ve actually found posts claiming that it is not possible to use IronPython with C++/cli.

Well, it is possible and easy, once you have a working example.  If, not then … well, lets just hope your hair will grow back.

Ok, I’m never one to take the easy way out, but I started by creating a C# dll that called my IronPython script and then called the C# dll from C++/cli.  That worked but seemed overly complicated.  If you do not mind a middleman then I guess it is ok, but if you want to go straight from C++/cli to IronPython, read on.

When I first tried to write a call to an IronPython script from  C++/cli, I tried a direct conversion of C# code into C++/cli code.  That didn’t work, so I then tried using RedGate’s .Net Reflector and the C++/cli add-in for .Net Reflector.  This got me 70% there.  Combining both with some trial and error got me the rest of the way. 

Funny, but when you look at the code, it seems so simple – yet getting there was not easy.

So, the following snippet shows a simple usage of IronPython as a scripting language.  It takes the first parameter passed in prints it to the console, passes it to the IronPython script: ipyStrings.ipy, then prints to the console the value of the same parameter that was passed back.  The IronPython code takes the string, prints it, reverses it, then sends it back.

C++/cli code:

Code Snippet
  1. int main(array<System::String ^> ^args)
  2. {
  3.     try
  4.     {
  5.         if(args->Length>0)
  6.         {   
  7.             String^ filename = "ipyStrings";
  8.             String^ path = Assembly::GetExecutingAssembly()->Location;
  9.  
  10.             ScriptEngine^ engine = Python::CreateEngine();
  11.             ScriptScope^ scope = engine->CreateScope();
  12.             ScriptSource^ source = engine->CreateScriptSourceFromFile
  13.             (String::Concat(Path::GetDirectoryName(path), "\\", filename, ".ipy"));
  14.        
  15.             Console::WriteLine(args[0]);
  16.  
  17.             scope->SetVariable("passedArgs", args[0]);           
  18.             source->Execute(scope);
  19.  
  20.             Console::WriteLine(scope->GetVariable<String ^>("passedArgs"));
  21.         }                                   
  22.     }
  23.     catch(Exception ^e)
  24.     {
  25.         Console::WriteLine(e->ToString());
  26.     }
  27.  
  28.     Console::ReadLine();
  29.  
  30.     return 0;
  31. }

Python code:

Code Snippet
  1. import clr
  2.  
  3. print "Passed in: " + passedArgs
  4. passedArgs = passedArgs[::-1]
  5.  
  6. print "Sending back: " + passedArgs

3 comments:

Anonymous said...

Unfortunately this doesn't work anymore with IronPython 2.7 and VS 2013, would be lovely if you could explain how to get it to work with the latest :D

Anonymous said...

Could you also explain how to call C# dll using C++/CLI . I am familiar with C++, not knowing C# and Ironpython.
But I actually met the same problem : I need to integrate a C# based dll to call Ironpython script in my C++ application.

thanks.

Anonymous said...

Could you also explain how to call C# dll using C++/CLI . I am familiar with C++, not knowing C# and Ironpython.
But I actually met the same problem : I need to integrate a C# based dll to call Ironpython script in my C++ application.

thanks.