Giter Club home page Giter Club logo

lul's Introduction

lul

This project shows an example of using lua as a scriptable ui logic component.

The binding is done via luabind and the dependency injection using wallaroo.

walkthrough

Dependency inversion

Starting from lul_example.cpp:

Catalog catalog;
lul::CreateSomeUI("MainUI",catalog);
lul::CreateTestLogic("TestLogic",catalog);

catalog is here the named object pool that can contain instances of the interfaces defined in lul/iui.h. There you will find an IView and an ILogic simple interfaces. The two lul::Create* calls instantiate and register the named objects that implement the interfaces. As you can see, the knowledge of the concrete types is not required here.

The implementation of the UI contains a placeholder for the logic component as a Wallaroo 'plug':

class SomeUI : public lul::iui::IView
{
public:
	SomeUI(): logic( "logic", RegistrationToken() ) {}

	// ...

private:
	wallaroo::Plug<lul::iui::ILogic> logic;
};

Now, the components are linked together using the special Wallaroo syntax that is quite natural language - readable:

wallaroo_within(catalog)
{
	use("TestLogic").as("logic").of("MainUI");
}

All objects are specified by their name, so you need discipline, error handling or a fool-proof configuration to resolve all the necessary dependencies. The 'TestLogic' logic implementation does nothing apart from echoing events to the console. Hence, when you call the following,

Using the interface instances

std::shared_ptr<IView> MainUI = catalog["MainUI"];
MainUI->ReceiveEvent("test");

the UI forwards the call to it's logic meber that echoes SomeUILogic received event: test.

reconfiguring the instances

The main strength of Wallaroo is perhaps the ability to reconfigure the dependency resolutions at runtime. Thus, calling

wallaroo_within(catalog)
{
	use("LuaCounterLogic").as("logic").of("MainUI");
	use("MainUI").as("view").of("LuaCounterLogic");
	use("LuaCounterContext").as("context").of("LuaCounterLogic");
}

updates the dependency of 'MainUI', and adds a new dependency of the dependency - the LuaCounterContext. What exactly is runtime-linked to what can be read in plain English from the code itself.

Now, the behavior of the UI is totally changed, and it uses two more components.

LuaCounterLogic

The logic component implemented in Lua just as an example. The Lua script text is embedded into the c++ executable for demo purposes. Any other kind of script deployment can replace this one. The logic component knows of the UI interface and of the state interface, which is provided in example/iluacontext.h. The interface is extermely simple - it can only accept a Lua state, into which it binds a C++ context class, which will be instantiated from Lua. The state is a simple counter with an upper and a lower limit.

The logic component exposes only three functions to the Lua state: UnknownEvent, Alert and Updated that are conveniently bound to the logic instance using LuaBind:

luaL_openlibs(L);
module(L) [
	def("UnknownEvent",
		tag_function<void(std::string)>(
			boost::bind(&LuaLogic::UnknownEvent,this,_1)
		)),
	//...
];

The events from the outside are forwarded to the state machine actions implementation, which is defined in context.lua.

the whole picture again

Coming back to the UI: the example program consists of a chain of calls to the ReceiveEvent method of the UI, simulating external input events. These are forwarded exactly to the logic, as the primary concern of the UI might be presentation solely.

Since the state machine is implemented in Lua, lua functions for the named actions are called if the event name is present. These, in turn, evaluate the state of the state machine, which is implemented in C++ and is bound to the Lua state.

During the action, the script calls some functions that report the update back to the UI. I.e. Updated and Alert:

function Increment()
	if context.state < context.upper_limit then
		context.state = context.state+context.increment;
		Updated( 'counter' , tostring(context.state) )
	else
		Alert( [[Upper limit of ]] .. context.upper_limit .. [[ reached]] )
	end
end

These updates are then forwarded back to the UI via the IView interface:

void Updated(std::string key,std::string value) const {
	if (view)
		view->ShowValue(key,value);
}

decoupling strategy and flexibility

This example is not intended as a recipe for implementing UIs with logic and state. It rather tries to demonstrate that, given independent components, you are much more free to restructure and reimplement your software architecture. For example, as in this example, having a state in C++ is not really needed need. To move that concern to the Lua side would be very simple - delete the binding and define one Lua table with number values in the script. There's no message passing strategy involved in the example, but incorporating it should not be a huge refactoring, since the interfaces are rather lean and mimic message passing.

license

No specific license is attached. Use at your own risk.

lul's People

Contributors

d-led avatar

Watchers

James Cloos avatar  avatar

Forkers

rodrigobmg

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.