OwlSL – C# like Scripting Language v0.0.2

I’ve fixed some bugs in the parser and added the posibility to expose templated types from the host application.
So now, for example, you can have a list type implemented in the host and use it with any type from the script.
For now class properties are private and can only be accesed through accesor methods.

Here an example of a script using the templated list type.

class test
{
	number num;
	void set(number val)
	{
		num = val;
	}
	number get()
	{
		return num;
	}
}

class Program
{
	void Main()
	{
		list<list<test>> lst;

		for (number t=0; t<1; t+=1)
		{
			lst.add(list<test>());
			for (number i=0; i<10; i+=1)
			{
				lst.at(t).add(test());
				lst.at(t).at(i).set(i);
			}
		}
		
		for (number t=0; t<1; t+=1)
		{
			for (number i=0; i<lst.at(t).count(); i+=1)
			{
				lst.at(t).print(lst.at(t).at(i).get().text());
			}
		}
	}
}

Source code for version 0.0.2 can be downloaded here: https://sourceforge.net/projects/owlsl/
Website: http://owlsl.blogspot.com

Leave a comment

Mixing std::map and std::deque into a convenient container

This may seem a little dumb, but it gets handy when you only know the key of the elements and you are going to access them often, for example, the symbols in a scripting engine.

I called the class ‘index’, but any other name could do.

#include <iostream>
#include <string>
#include <map>
#include <deque>

using namespace std;

template<class name_type, class item_type>
class index
{
    public:
        int32_t add(const name_type& name, const item_type& value)
        {
            if ((item = indexes.find( name ))!= indexes.end())
            {
                return -1;
            }

            indexes.insert(std::make_pair(name, 0));
            item = indexes.find( name );
            names.push_back( const_cast<name_type*>(&((*item).first)) );
            items.push_back(value);
            (*item).second = items.size()-1;

            return (*item).second;
        }

        int32_t get_index(const name_type& name)
        {
            if ((item = indexes.find( name ))!= indexes.end())
            {
                return (*item).second;
            }

            return -1;
        }

        const name_type& get_name(const size_t& index)
        {
            return *names[index];
        }

        item_type& get(const size_t& index)
        {
            return items[index];
        }

        item_type& get(const name_type& name)
        {
            return get(get_index(name));
        }

        const size_t size()
        {
            return items.size();
        }

        void clear()
        {
            indexes.clear();
            names.clear();
            items.clear();
        }

    protected:
        std::map<name_type, size_t> indexes;
        std::deque<name_type*>      names;
        std::deque<item_type>       items;

        typename std::map<name_type, size_t>::iterator item;
};

How to use it:

int main()
{
    index<std::string, std::string> idx;
    idx.add("first", "This is the first element");
    idx.add("second", "This is the second element");
    idx.add("dunno", "Not sure which is the index of this one");

    std::cout << "key: " <<idx.get_name(0) << ", index: " << idx.get_index("first") << ", value: " << idx.get(0) <<std::endl;
    std::cout << "key: " <<idx.get_name(1) << ", index: " << idx.get_index("second") << ", value: " << idx.get(1) <<std::endl;
    std::cout << "key: " <<idx.get_name(2) << ", index: " << idx.get_index("dunno") << ", value: " << idx.get(2) <<std::endl;

    return 0;
}

Output:

key: first, index: 0, value: This is the first element
key: second, index: 1, value: This is the second element
key: dunno, index: 2, value: Not sure which is the index of this one

Leave a comment

Weird segmentation faults compiling with MinGW, threads and DLLs?

You may be aware that in order to use thread exceptions safely you must compile passing the “-mthreads” option. What I didn’t know was that I also had to pass it to the linker. I realized this when I moved some code from the executable to a separate dll. I started getting segmentation faults in std containers and strings.

If you are experiencing something like this pass the

-mthreads

option to the compiler and

-mthreads

option to the linker.

In Code::Blocks go to “Settings”->“Compiler And Debugger…” menu.
Then “Compiler Settings” tab, “Other Options” sub-tab.
And write: -mthreads

Then go to the “Linker settings” tab, “Other linker options” text area and write:
-mthreads

Re compile your binaries and if that was your problem you should be ready for the next bug.

Good luck!

Leave a comment

Adding AngelScript scripting to your C++ application

I’ve been using Lua for a while and it was nice until I started having problems with garbage collection and some strange leakage I couldn’t figure out how to solve. Thus, using all the experience I gathered from implementing Lua I jumped into the world of AngelScript and it wasn’t painful at all.

This snippet is intended for AngelScript 2.23.1. As always, you need to download the library, compile it if you must (it comes with handy project files for most IDEs) and link it to your project properly. ALSO note that the includes that lie in the “add_on” folder come with the AngelScript library source code. You must have them somewhere so you can include them properly.

As I did with Lua, I wrapped the library into classes of my own which are engine to handle general stuff, module to handle scripts specific stuff and callback to call functions declared in AngelScript script files.

These snippets are anything but complete. I can foresee them becoming bigger and bloated, so I’ll post what I got till now, so it can be easier for you to see what’s going on under the hood.

So, the engine class:

#include <string>
#include <vector>
#include <boost/smart_ptr.hpp>
#include <angelscript.h>
#include "add_on/scripthandle/scripthandle.h"
#include "add_on/scriptstdstring/scriptstdstring.h"
#include "module.h"
#include "callback.h"

class engine
{
    friend class module;
    friend class callback;

    public:

        engine()
        {
            m_engine = NULL;
            m_has_compile_errors = false;
        }

        virtual ~engine()
        {
            terminate();
            std::cout << "Engine destructor called." <<std::endl;
        }

        bool init()
        {
            if( m_engine )
            {
                return false;
            }

            int r;

            m_engine = asCreateScriptEngine(ANGELSCRIPT_VERSION);

            // Set the message callback to print the human readable messages that the engine gives in case of errors
            r = m_engine->SetMessageCallback(asMETHOD(engine, message_callback), this, asCALL_THISCALL); assert( r >= 0 );

            // Register the string type
            RegisterStdString(m_engine);

            // Register the generic handle type, called 'ref' in the script
            RegisterScriptHandle(m_engine);

            // instance some contexts to have them at hand
            for( unsigned int n = 0; n < 10; n++ )
            {
                contexts.push_back(m_engine->CreateContext());
            }

            m_has_compile_errors = false;

            return true;
        }

        void terminate()
        {
            if( !m_engine )
            {
                return;
            }

            for( unsigned int n = 0; n < contexts.size(); n++ )
            {
                contexts[n]->Release();
            }

            m_engine->Release();

            m_engine = NULL;
        }

        // When we want a new module, we create it through here
        module_ptr new_module(const std::string& module_name = "")
        {
            return module_ptr(new module(this, module_name));
        }

        // There are many ways in AngelScript to register your classes, this is one of them
        template <class class_type>
        bool register_class(const std::string& class_name)
        {
            if (m_engine->RegisterObjectType(class_name.c_str(), sizeof(class_type), asOBJ_VALUE | asOBJ_POD)<0)
            {
                std::cout << "Error - engine::register_class(): Can't register class " + class_name <<std::endl;
                return false;
            }
            return true;
        }

        // This is how you register a C++ class method in AngelScript
        bool register_class_method(const std::string& class_name, const std::string& function_definition, const asSFuncPtr &funcPointer)
        {
            if (m_engine->RegisterObjectMethod(class_name.c_str(), function_definition.c_str(), funcPointer, asCALL_THISCALL)<0)
            {
                std::cout << "Error - engine::register_class(): Can't register class method " + class_name + "::" + function_definition <<std::endl;
                return false;
            }
            return true;
        }

    protected:
        
        // This is used for AngelScript error messages
        void message_callback(const asSMessageInfo &msg)
        {
            const char *type = "ERR ";
            if( msg.type == asMSGTYPE_WARNING )
            {
                type = "WARN";
            }
            else if( msg.type == asMSGTYPE_INFORMATION )
            {
                type = "INFO";
            }

            std::cout << msg.section << " (" << msg.row << ", " << msg.col << ") : " << type << " : " << msg.message << std::endl;

            if( msg.type == asMSGTYPE_ERROR )
            {
                m_has_compile_errors = true;
            }
        }
        
        // Contexts is what you use to call AngelScript functions and methods. They say you must pool them to avoid overhead. So I do as they say.
        asIScriptContext* new_context()
        {
            asIScriptContext *ctx = 0;
            if( contexts.size() )
            {
                ctx = *contexts.rbegin();
                contexts.pop_back();
            }
            else
            {
                ctx = m_engine->CreateContext();
            }

            return ctx;
        }
        
        // After you're done calling your AngelScript function, you get the context back into the pool for re-use
        void free_context(asIScriptContext *ctx)
        {
            contexts.push_back(ctx);
            ctx->Unprepare();
        }
        
        // This is what we will call from the callback class to execute AngelScript Functions and Methods
        int execute_call(asIScriptContext *ctx)
        {
            int r = ctx->Execute();
            if( r != asEXECUTION_FINISHED )
            {
                if( r == asEXECUTION_EXCEPTION )
                {
                    std::cout << "Exception: " << ctx->GetExceptionString() << std::endl;
                    std::cout << "Function: " << ctx->GetExceptionFunction()->GetDeclaration() << std::endl;
                    std::cout << "Line: " << ctx->GetExceptionLineNumber() << std::endl;

                    // It is possible to print more information about the location of the
                    // exception, for example the call stack, values of variables, etc if
                    // that is of interest.
                }
            }

            return r;
        }

        // This should free all scripting that lies into a specific module
        void unload_module(const std::string& module_name = "")
        {
            int r = m_engine->DiscardModule(module_name.c_str());
            if (r<0)
            {
                std::cout << "Error - engine::terminate_module(): Can't DiscardModule " + module_name <<std::endl;
            }
        }

        bool                m_has_compile_errors; // This was in the example I took the code from but serves no purpose here except for writing this comment.
        asIScriptEngine*    m_engine;             // The AngelScript engine instance
        std::vector<asIScriptContext*> contexts;  // The pool of contexts for function calling (it's used by the callback class)
};

typedef boost::shared_ptr<engine> engine_ptr; // Declare a shared pointer just in case you ever need it.

The module class. AngelScript arranges scripts into modules for scoping. Script files grouped into a module behave as a big single script.

#include <boost/smart_ptr.hpp>
#include <angelscript.h>

class engine;

class module
{
    friend class engine;

    public:
        module(engine* eng, const std::string& module = "") : m_engine(eng), m_module_name(module_name)
        {
            m_module = m_engine->m_engine->GetModule(m_module_name.c_str(), asGM_CREATE_IF_NOT_EXISTS);
        }
        
        virtual ~module() {}
        
        bool load_script(const std::string& file_path)
        {
            /*
                THE CLASS "file" IS JUST AN UTILITY CLASS THAT LOADS A FILE INTO A BUFFER AND TELLS 
                HOW BIG IT IS (AMONG OTHER STUFF). IT IS QUITE LARGE SO I'M NOT POSTING IT HERE.
            */
            *YOU MUST WRITE THE FILE LOADING YOURSELF*
            
            file script_file;
            if (!script_file.open(file_path))
            {
                std::cout << "Error - engine::load(): Can't load file " + file_path <<std::endl;
                return false;
            }

            return load_script(&script_file);
        }        
        
        bool load_script(file* script_file)
        {
            int r = m_module->AddScriptSection(script_file->file_path().string().c_str(), &script_file->buffer()[0], script_file->size());

            if( r < 0 )
            {
                std::cout << "AddScriptSection() failed" << std::endl;
                return false;
            }

            return true;
        }        

        bool build()
        {
            // Compile the script. If there are any compiler messages they will
            // be written to the message stream that we set right after creating the
            // script engine. If there are no errors, and no warnings, nothing will
            // be written to the stream.
            int r = m_module->Build();
            if( r < 0 )
            {
                std::cout << "Build() failed" << std::endl;
                return false;
            }

            return true;
        }
        
        void unload()
        {
            if (m_module)
            {
                m_engine->unload_module(m_module_name);
            }
        }


        const std::string& name()
        {
            return m_module_name;
        }

    protected:

        engine*             m_engine;
        asIScriptModule*    m_module;
        std::string         m_module_name;
};
typedef boost::shared_ptr<module> module_ptr;

The callback class:

#include <string>
#include <boost/smart_ptr.hpp>
#include <angelscript.h>

class module;
class engine;

class callback
{
    public:
        callback(engine* eng) : m_engine(eng)
        {
            m_function_id = 0;
            m_context = NULL;
            m_object_address = NULL;
        }
        
        virtual ~callback()
        {

        }

        void init(module* mod, const std::string& function_declaration)
        {
            m_module = mod;
            m_function_id = m_engine->m_engine->GetModule(m_module->name().c_str())->GetFunctionIdByDecl(function_declaration.c_str());
        }
        
        void call_begin()
        {
            assert( m_function_id > 0 );
            m_context = m_engine->new_context();
            int r = m_context->Prepare(m_function_id); assert( r >= 0 );
            if (m_object_address)
            {
                m_context->SetObject(m_object_address);
            }
        }
        
        void call_end()
        {
            m_engine->execute_call(m_context);
            m_engine->free_context(m_context);
        }

    protected:

        engine*             m_engine;
        module*             m_module;
        void*               m_object_address;
        int                 m_function_id;
        asIScriptContext*   m_context;
};
typedef boost::shared_ptr<callback> callback_ptr;

The AngelScript source code:

// main.as
void main()
{
    console con;
    con.write_line("hola mundo!");
}
// test.as
void test()
{
    console con;
    con.write_line("this comes from test!");
}

And how to use everything:

#include <iostream>
#include "engine.h"

class console
{
    public:
        void write_line(const std::string& text)
        {
            std::cout << text <<std::endl;
        }
};

int main()
{
    engine my_engine;
    my_engine.init();

    // Tell AngelScript to use the class "console" we just declared above
    my_engine.register_class<console>("console");
    my_engine.register_class_method("console", "void write_line(string &in)", asMETHOD(console, write_line));

    module_ptr my_module = engine.new_module();
    my_module->load_script("main.as");
    my_module->load_script("test.as");
    my_module->build();

    {
        callback my_callback(&engine);
        my_callback.init(my_module.get(), "void main()");
        my_callback.call_begin();
        my_callback.call_end();
    }

    {
        callback my_callback(&engine);
        my_callback.init(my_module.get(), "void test()");
        my_callback.call_begin();
        my_callback.call_end();
    }    
    return 0;
}

Output:

hola mundo!
this comes from test!
Engine destructor called.

That’s it. I hope this helps you getting started with this awesome scripting library. There is an entire forum at gamedev.net devoted to discuss this library. You may wanna check it out.

Note: The module and callback classes forward declare stuff. You must separate the declaration and the definition (.h, .cpp) in order for this to compile. Also, don’t forget to implement your own file loading routine.

Bye!

Leave a comment

Serialize your class to a widechar XML file (with boost::serialization)

This is an old snippet I posted on gamedev.net a while back. If I remember correctly it worked fine at the time so it should still work.

#include <iostream>
#include <fstream>
#include <string>
#include <boost/archive/xml_wiarchive.hpp>
#include <boost/archive/xml_woarchive.hpp>

class user
{
    friend class boost::serialization::access;
    public:
        void set(const std::string& name, const std::string& lastname, int birthday)
        {
            m_name     = name;
            m_lastname = lastname;
            m_birthday = birthday;
        }

        std::string get_name()     { return m_name; }
        std::string get_lastname() { return m_lastname; }
        int         get_birthday() { return m_birthday; }

    private:
        std::string m_name;
        std::string m_lastname;
        int         m_birthday;

        template<class Archive>
        void serialize(Archive& ar, const unsigned int version)
        {
            ar & BOOST_SERIALIZATION_NVP(m_name)
               & BOOST_SERIALIZATION_NVP(m_lastname)
               & BOOST_SERIALIZATION_NVP(m_birthday);
        }
};
BOOST_CLASS_VERSION(user, 1)

Usage:

int main(int argc, char* argv[])
{
    // Save user
    {
        user u1;
        u1.set("Sebastián Ramón", "Perez Azaroño", 19780520);
        std::wofstream ofs("serialize.xml");
        boost::archive::xml_woarchive oa(ofs);
        oa << BOOST_SERIALIZATION_NVP(u1);
    }

    // Load user
    {
        user u2;
        std::wifstream ifs("serialize.xml");
        boost::archive::xml_wiarchive ia(ifs);
        ia >> BOOST_SERIALIZATION_NVP(u2);

        std::cout << u2.get_name() << std::endl;
        std::cout << u2.get_lastname() << std::endl;
        std::cout << u2.get_birthday() << std::endl;
    }

    return 0;
}

Output:

Sebastián Ramón
Perez Azaroño
19780520

Leave a comment

Errors passing std::string among DLLs – GCC/MinGW32

This isn’t really a snippet but an advice to those who want to pass std::string to functions/methods lying on a dll. This also applies for classes that make use of std::string like boost::filesystem::path which might cause random break-points when debugging with gdb.

If you want to avoid runtime errors you need to use MinGW compiled with –enable-fully-dynamic-string.

One place to get it already compiled is here:

http://code.google.com/p/mingw-builds/downloads/detail?name=mingw32-gcc-4.6.1-release-20110830.7z

Good Luck!

Leave a comment

Avoid application freezing when doing multithreading on win32

I’ve been experiencing difficulties when rendering a window on a different thread (Windows 7). The application wouldn’t stop getting ghosted (not responding).

I found out that just calling:

    MSG		msg;
    if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

in each thread loop that is using too much cpu time solves the problem.

Hope that helps!

Leave a comment

Follow

Get every new post delivered to your Inbox.