Back

 

Overview 1

Sample Code 6

Code Integration 6

Using CPB 7

CPB Internals 8

FAQ 9

Overview

 

CPB works on the concept of binding C++ data in to LUA. C++ variables, functions or classes are brought in to LUA to be manipulated by the scripting language.

 

First we must is to choose what data we want CPB to bind from the host program. We do this using the symbol’s name which we can specify in bulk using wild card matching. Once the bindings have been made, any changes to the variable and classes in LUA will affect the C++ parents in real time. 

 

The following is a walkthrough of all the different supported types from the data perspective of the C++ files, LUA and finally the output.

 

Constants

 

C++ Values

 

const float         cn_Float    = 123.233f;

const int           cn_Int      = 999;

const short         cn_Short    = 12312;

enum {          cn_Enum   = 666};

 

LUA Script

 

CP_BindSymbol("cn_*");  -- Bind the constants starting with cn_

      

print ("CPTest.cn_Int=   ",CPTest.cn_Int)

print ("CPTest.cn_Short= ",CPTest.cn_Short)    

print ("CPTest.cn_Float= ",CPTest.cn_Float)

print ("CPTest.cn_Enum=  ",CPTest.cn_Enum)     

 

Output

 

CPTest.cn_Int=          999

CPTest.cn_Short=        12312

CPTest.cn_Float=        123.233

CPTest.cn_Enum=         666

 

 

Since these are constants, if you change these values from LUA, they will not affect the C++ version of the data, but you do have a read only access to the data.

 

#defines are not supported, since these do not officially exist in the final program. Please use constants or enumerations instead.

 

Basic Variables

 

C++ Values

 

char*               cp_String   = "Non Const String Data";

char                cp_Byte     = 111;

unsigned char       cp_UByte    = 222;

int                 cp_Int      = 111;

unsigned int        cp_UInt     = 222;

long                cp_Long     = 333;

unsigned long       cp_ULong    = 444;

short               cp_Short    = 555;

unsigned short      cp_UShort   = 666;

float               cp_Float    = 777.777f;

 

LUA Script

print ("\n====  Integer Variables Types ==== ")

CP_BindSymbol("cp_*"); 

-- Binds all variables that starts with the words cp_

 

print ("CPTest.cp_Int = ",CPTest.cp_Int, "Attempting -133")  

CPTest.cp_Int = -133

print ("CPTest.cp_Int = ",CPTest.cp_Int, "Attempting: CPTest.cn_Enum")

CPTest.cp_Int = CPTest.cn_Enum

print ("CPTest.cp_Int = ", CPTest.cp_Int)      

print ("");

      

print ("CPTest.cp_UInt = ",CPTest.cp_UInt, "Attempting -1")

CPTest.cp_UInt = -1

print ("CPTest.cp_UInt = ",CPTest.cp_UInt, "Adding 1")

CPTest.cp_UInt = CPTest.cp_UInt+1

print ("CPTest.cp_UInt = ",CPTest.cp_UInt)

print ("");

 

print ("CPTest.cp_Short= ",CPTest.cp_Short, "Attempting 1600")      

CPTest.cp_Short = 16000

print ("CPTest.cp_Short= ",CPTest.cp_Short, "Attempting 6400000")

CPTest.cp_Short = 6400000 

print ("CPTest.cp_Short= ",CPTest.cp_Short)

print ("");

 

print ("\n====  Float Variables Types ==== ")

print ("CPTest.cp_Float= ",CPTest.cp_Float, "Attempting 123.1230")  

CPTest.cp_Float = 123.1230

print ("CPTest.cp_Float= ",CPTest.cp_Float, "Attempting 123")

CPTest.cp_Float = 123

print ("CPTest.cp_Float= ",CPTest.cp_Float)

print ("");

Output

 

====  Integer Variables Types ====

CPTest.cp_Int =         111     Attempting -133

CPTest.cp_Int =         -133    Attempting: CPTest.cn_Enum

CPTest.cp_Int =         666

 

CPTest.cp_UInt =        222     Attempting -1

CPTest.cp_UInt =        4294967295      Adding 1

CPTest.cp_UInt =        0

 

CPTest.cp_Short=        555     Attempting 1600

CPTest.cp_Short=        16000   Attempting 6400000

CPTest.cp_Short=        -22528

 

====  Float Variables Types ====

CPTest.cp_Float=        777.777     Attempting 123.1230

CPTest.cp_Float=        123.123     Attempting 123

CPTest.cp_Float=        123

 

 

Not all the different variable types have been shown here. For a more extensive listing please check the example program.

 

C Functions

 

C Functions

 

char* ff_Test_RF(float a_Value1, double a_Value2, float a_Value3, double a_Value4);

bool  ff_Test_RI(char* a_Value1, int a_Value2, short a_Value3, unsigned short a_Value4);

void ff_Test_Char(char a_Value1, wchar_t a_Value2, HRESULT a_Value3, bool a_Value4);

 

void ff_Test_StructParamByValue(TestStruct a_Value1);

void ff_Test_StructParamByPointer(TestStruct* a_Value1);

void ff_Test_StructParamByReference(TestStruct& a_Value1);

 

LUA Script

 

CP_BindSymbol("ff_*")  -- Now bind a function

 

print("\n=== Calling a function with four float parameters====")    

local value = CPTest.ff_Test_RF(11.11, 22.22, 33.33, 44.44)

print("return value =", value);

 

print("\n=== Calling a function with a string and integer ====") local value = CPTest.ff_Test_RI(value, 40000, 128, 20000)

print("return value =", value);

 

print("\n=== Calling a function ints and bool ====")  

local value = CPTest.ff_Test_Char(1, 2, 3, true)

print("return value =", value);  

 

print("\n=== Calling a function with LUA string ====")

local value = CPTest.ff_Test_StructValue("Structure text")

 

print("\n=== Calling functions with various types of Params====")

CPTest.ff_Test_StructParamByValue(value)

CPTest.ff_Test_StructParamByPointer(value)

CPTest.ff_Test_StructParamByReference(value)

CPTest.ff_Test_StructParamByPointer(value)

 

Output

 

=== Calling a function with four float parameters====

ff_Test_RF : 11.110000 : 22.220000 : 33.330002 : 44.440000

return value =  userdata: 0044306C

 

=== Calling a function with a string and some integers====

ff_Test_RI: My Test String : 40000 : 128 : 20000

return value =  true

 

=== Calling a function ints and bool ====

ff_Test_Char*:  1 : 2 : 3 : 1

return value =  nil

 

=== Calling a function with LUA string ====

Paramter: Structure text

 

=== Calling functions with various types of Params====

ByValue:  Structure text : 1234 :

ByPointer*:  Structure text : 1234

ByReference&:  Changed ByPointer! : 4321

ByPointer*:  Changed ByReference! : 7890

 

As shown by the above, most permutations of parameter passing, and return values are covered. You can still really confuse CPB if you mix enough esoteric C++ features but mostly you should be okay.

 

C++ Class Variables and Functions

 

 

C++ Values

class CL_TestClass2

    {

    public:

        int m_Value1;

        int m_Value2;

    };

 

class CL_TestClass

    {

    public:

   

    CL_TestClass();

 

    void set1(void);

    void set2(void);

    void ClassFunction(int a_Param1, int a_Param2);

    static void StaticFunction(int a_Param1);

 

    CL_TestClass2* GetNestedPointer();

    CL_TestClass2 GetNestedObject();

 

    CL_TestClass2 m_Nested;   

    CL_TestClass* m_pSelf;

 

    int m_Value1;

    int m_Value2;

    char* m_pString;

    };

 

CL_TestClass cc_Class1;

CL_TestClass cc_Class2;

 

LUA Script

 

CP_BindSymbol("cc_*");  -- Bind some classes

 

print("\n=== Member Variables in a Class ====")

print ("CPTest.cc_Class1=  ",CPTest.cc_Class1)

print ("CPTest.cc_Class1.m_Value1=  ",CPTest.cc_Class1.m_Value1)

print ("CPTest.cc_Class1.m_Value2=  ",CPTest.cc_Class1.m_Value2)

 

print("\n=== Nested Class Variables====")

print ("CPTest.cc_Class1.m_Nested.m_Value1=  ",CPTest.cc_Class1.m_Nested.m_Value1)

print ("CPTest.cc_Class1.m_Nested.m_Value2=  ",CPTest.cc_Class1.m_Nested.m_Value2)

 

print("\n=== Member Functions in a Class ====")              

CPTest.cc_Class1:ClassFunction(12, 23);

CPTest.cc_Class2:set2();

CPTest.cc_Class1.StaticFunction(78);           

 

print("\n=== Different Member Variables in Same Class Type ===");

print ("CPTest.cc_Class2.m_Value1=  ",CPTest.cc_Class2.m_Value1)

print ("CPTest.cc_Class2.m_Value2=  ",CPTest.cc_Class2.m_Value2)

print ("CPTest.cc_Class2.m_Nested.m_Value1= ",CPTest.cc_Class2.m_Nested.m_Value1)          

print ("CPTest.cc_Class2.m_Nested.m_Value2= ",CPTest.cc_Class2.m_Nested.m_Value2)                        

CPTest.cc_Class2:ClassFunction(34, 56);                               

 

Output

 

=== Member Variables in a Class ====

CPTest.cc_Class1=       userdata: 0089E6B8

CPTest.cc_Class1.m_Value1=      111

CPTest.cc_Class1.m_Value2=      222

 

=== Nested Class Variables====

CPTest.cc_Class1.m_Nested.m_Value1=     333

CPTest.cc_Class1.m_Nested.m_Value2=     444

 

=== Member Functions in a Class ====

ClassFunction Called - 12 23 : 111 222

Set2 Called

StaticFunction Called - 78

 

=== Different Member Variables in Same Class Type ===

CPTest.cc_Class2.m_Value1=      555

CPTest.cc_Class2.m_Value2=      666

CPTest.cc_Class2.m_Nested.m_Value1=     777

CPTest.cc_Class2.m_Nested.m_Value2=     999

ClassFunction Called - 34 56 : 555 666

 

 

As soon as a variable which is a class is bound, CPB will also proceed to also bind all of its member classes, variables and functions. This is NOT the case with pointers to classes.  For particular heavy classes this could result in many unintended classes being exposed.

 

Pointer to Classes

 

 

C++ Values

 

class CL_TestClass2

    {

    public:

        int m_Value1;

        int m_Value2;

    };

 

class CL_TestClass

    {

    public:

   

    CL_TestClass();

 

    void set1(void);

    void set2(void);

    void ClassFunction(int a_Param1, int a_Param2);

    static void StaticFunction(int a_Param1);

 

    CL_TestClass2* GetNestedPointer();

    CL_TestClass2 GetNestedObject();

 

    CL_TestClass2 m_Nested;   

    CL_TestClass* m_pSelf;

 

    int m_Value1;

    int m_Value2;

    char* m_pString;

    };

 

CL_TestClass* co_pClass1;

 

LUA Script

CP_BindSymbol("CL_TestClass");    -- Bind a Class Symbol

CP_BindSymbol("co_*");            -- Bind pointer to classes                     

print("\n=== Get Pointer to Objects ==== ")

newPointer = CPTest.co_pClass1:GetNestedPointer()

print("newPointer = CPTest.co_pClass1:GetNestedPointer()")

print("newPointer.m_Value1=  ",newPointer.m_Value1)

 

print("\n=== Pointers to a classes ==== ")

print ("CPTest.co_pClass1=  ", CPTest.co_pClass1)     

print ("CPTest.co_pClass1.m_Value1=  ",CPTest.co_pClass1.m_Value1)

print ("CPTest.co_pClass1.m_Value2=  ",CPTest.co_pClass1.m_Value2)

CPTest.co_pClass1:ClassFunction(12, 23);

 

print("\n=== Pointers to a classes with nested values ==== ")

print ("CPTest.co_pClass1.m_Nested.m_Value1=  ",CPTest.co_pClass1.m_Nested.m_Value1)         

print ("CPTest.co_pClass1.m_Nested.m_Value2=  ",CPTest.co_pClass1.m_Nested.m_Value2)                       

 

print("\n=== Circular Pointers to Self ==== ")

print ("CPTest.co_pClass1.m_pSelf.m_pSelf.m_pSelf.m_pSelf.m_Value1=  ",CPTest.co_pClass1.m_pSelf.m_pSelf.m_pSelf.m_pSelf.m_Value1)

 

Output

 

=== Get Pointer to Objects ====

newPointer = CPTest.co_pClass1:GetNestedPointer()

newPointer.m_Value1=    333

 

=== Pointers to a classes ====

CPTest.co_pClass1=      userdata: 0089F308

CPTest.co_pClass1.m_Value1=     111

CPTest.co_pClass1.m_Value2=     222

ClassFunction Called - 12 23 : 111 222

 

=== Pointers to a classes with nested values ====

CPTest.co_pClass1.m_Nested.m_Value1=    333

CPTest.co_pClass1.m_Nested.m_Value2=    444

 

=== Circular Pointers to Self ====

CPTest.co_pClass1.m_pSelf.m_pSelf.m_pSelf.m_pSelf.m_Value1= 111

 

 

 

Pointers don’t get expanded out unless you specifically do a separate class binding or if one has already been done in a previous operation. This is there are a lot of pointers you need to store, but will not need to actually use.

Sample Code

A pre-built executable “RunTime/CPTest.exe” serves as an example of how CPB works.  The source code and project for the sample application is found at CPTest/main.cpp. The solution found in the root directory CPB.sln will build the test program.

Code Integration

CPB is distributed in source code form, so simply add the following CPB files to your project. You will require the other headers in the CPB directory.

 

        CPPDB.cpp

        CPBind.cpp

        CPAssembly.cpp

              

Include CPB.h in to your program to access the core CPB access function.

 

        int luaopen_CPB(lua_State *L);

 

Link in the following MSVC.net Library files

 

        comsupp.lib

        diaguids.lib   (Release DIA GUIDS)

 

For Debug use the following library instead

        diaguidsd.lib  (Debug DIA GUIDS)

 

diaguids are library files that are deployed with the DIASDK on MSVC.net. Not everyone installs them so I’ve included them with the CPB source files

 

Ensure the following DLL is in your runtime directory

        msdia71.dll

 

Under project properties ensure that Linker->Debugging->GenerateDebugInfo is set to Yes (/DEBUG) and the debug info type is Program Database. You will need to enable this for both your debug and release builds. While you compile your program you should see a file called “your_program_name.pdb” CPD uses this data file to generate binding data for your.

Using CPB

CPB exposes additional functions to LUA. These are added via the single integration function luaOpen_CPB().  You can see that in the following code snippet that you would have in your c program.(main.cpp in this example)

 

lua_State* L = lua_open();   

luaopen_base(L);

 

// Bind CPB LUA functions           

luaopen_CPB(L);

 

// Run the test file

lua_dofile(L, "test.lua");

lua_close (L);

 
 

From your lua script four new functions exported that you is used to define which symbols you wish to bind.

 

CP_OpenPDB    (char* a_pSymbolFile)        

CP_BindSymbol (char* a_pSymbolName)

CP_WriteCPB   (char* a_pCPBFile) or CP_WriteCPB()

CP_ClosePDB()

 

 

CP_OpenPDB needs to be called at the creation of the LUA State.  If you only use a single LUA state, you should just call this once. In the example program, it is done at the beginning of the script. You don’t want to do it like this if you have a lot of scripts that run off the same LUA state.

 

CP_BindSymbol is used to create the bindings that allow you to access the variables, functions and classes from LUA. This single function works for any symbol in your program and support basic ? and * wildcards. ? Matches a single character, * matches multiple.

 

While CPB could automatically bind everything, it’s wouldn’t be efficient, and would expose hundreds of unneeded symbols to your program.

 

 

CP_OpenPDB ("CPTest")    

CP_BindSymbol("cp_*") -- Binds all variables starting with cp_

 

CPTest.cp_Int = -133

CPTest.cp_UInt = -1

CPTest.cp_UInt = CPTest.cp_UInt+1 

 

 

All variables and functions are stored in a table based on the PDB name. The above example shows that once bound, you can easily access any variable or function by name.

 

 

CP_BindSymbol("ff_*")  -- Now bind any function with characters ff_

 

local value = CPTest.ff_Test_RF(11.11, 22.22, 33.33, 44.44)

print("return value =", value);

 

local value = CPTest.ff_Test_RI(value, 40000, 128, 20000)

print("return value =", value);

 

CP_ClosePDB ()  

 

 

CP_ClosePDB

Remember to call CP_ClosePDB at the destruction of your LUA state to close down your CPB bindings. In our example we do it at the end of the script.

 

CP_WriteCPB

You have should now full access to your variables but at some stage you’ll want to distribute this program to others, and you won’t want to also distribute your PDB file. You will want to generate a .cpb file. You can do this from LUA using CP_WriteCPB.

 

 

Once you’ve bound all the data you require for your script, call the above function to write out a CPB file. Once this is done subsequent runs of the program can use the CPB file instead of a PDB file. You can safely release your application with just the CPB file.

 

This has some restrictions. The CPB file only knows about the variables, functions and classes that were exposed at the time of its creation. In addition if you further modify your program, it will generally invalidate the existing .cpb file, so you only want to do this as a final release step.

CPB Internals

CPB differs from many existing binding libraries because it works at runtime on a binary level with your program.  It allows you to create bindings without modifying existing code because CPB uses data from your program’s symbol database or PDB files.

 

These files contain the all symbol information for a program such as memory addresses, type information for variables, classes, functions and enumerations. This gives CPB a blueprint of the entire program in memory.

 

Using this information CPB internally generates the appropriate binding code that you normally don’t see.  Normally when you compile your program in debug mode you get an executable and a PDB file. Remember to enable PDB generation for release builds also so CPB can function in release mode. 

 

FAQ

I just added a new function and variable, and CPB crashes trying to access it.

 

Generally this occurs because the compiler has decided to optimize out your particular variable or function.  If no code in your program touches a variable for function, as far as your program is concerned it's dead weight since it assumes it has no use, and does not know you actually want to call this functionality from your LUA script. 

 

The best solution is to prefix the variable or function with __declspec(dllexport), or to lightly reference the data in some way. This will mark the data as an export, and the compiler not to optimize it out even if no one is using it.

 

Another heavy handed approach would be to disable symbol stripping. It’s useful to enable to do a quick sanity check if you’re having trouble.

 

Project->Properties->Linker->Optimization->References->Keep Unreferenced Data

 

Does this work in Release mode as well as Debug?

 

Yes, but you do have to enable the generation of debug information.

 

Project->Properties->Linker->Debugging->Generate Debug Info->Yes

 

This does not change your executable in any way.  It just generates the PDB file that CPB needs in order to work. Remember to delete the PDB file as part of your release process, and ship the .cpb file instead.

 

Is this library x86 and Microsoft specific?

 

The current release is currently on targeted to this platform. Similar functionality that CPB requires to function is available on all platforms and compilers. Ports to those other systems will begin once core CPB development has stabilized.

 

The DIA COM component fails on initialization.

To access the DIA COM component that CPB depends on, your program will need to first register msdia71.dll (Microsoft Debug Interface Access).  On many systems it should exist, but on some older systems it may not been installed.

 

On the command prompt type "regsvr32.exe msdia71.dll" in the runtime directory to register it or do it as part of your main program executable.