Jump to content
  • entries
    948
  • comments
    5,905
  • views
    945,324

One Little Thing


A couple weeks ago I replaced the Object::SetHook() function with an Object::AddHook() function. The difference is subtle but significant. SetHook() supported a single hook function but AddHook() supports multiple hook functions for each hook ID, that will be called in sequence.

Syntax

  • AddHook(const int& hookid, void* hook)

Example

#include "App.h"

using namespace Leadwerks;

App::App() : window(NULL), context(NULL), world(NULL), camera(NULL) {}

App::~App() { delete world; delete window; }

void Hook1(Entity* entity) { System::Print("Hook 1"); }
void Hook2(Entity* entity) { System::Print("Hook 2"); }
void Hook3(Entity* entity) { System::Print("Hook 3"); }

bool App::Start()
{
   window = Window::Create();
   context = Context::Create(window);
   world = World::Create();
   camera = Camera::Create();
   DirectionalLight::Create()->SetRotation(35,45,0);

   //Create a model
   model = Model::Box();
   model->SetPosition(0,0,3);

   //Add some hooks
   model->AddHook(Entity::UpdateMatrixHook,Hook1);
   model->AddHook(Entity::DrawHook,Hook2);
   model->AddHook(Entity::DrawEachHook,Hook3);

   //Remove one hook
   model->RemoveHook(Entity::DrawEachHook,Hook3);

   return true;
}

bool App::Continue()
{
   if (window->Closed() || window->KeyDown(Key::Escape)) return false;

   //Make the model spin
   model->Turn(0,Time::GetSpeed()*1.0,0);

   Time::Update();
   world->Update();
   world->Render();
   context->Sync();

   return true;
}

 

Inside the engine, this just uses a multimap, which has some very complicated syntax that I could not type from memory:

            //Call hooks
           std::pair <std::multimap<int,void*>::iterator, std::multimap<int,void*>::iterator> ret;
           ret = (*entity)->hooks.equal_range(Entity::UpdateWorldHook);
           for (std::multimap<int,void*>::iterator it=ret.first; it!=ret.second; ++it)
           {
               void (*hook)(Entity*) = (void (*)(Entity*))(it->second);
               hook(*entity);
           }

So What's the Big Deal?

It's important for the API to be future-compatible. It's okay for new commands to be added, but once the first release is out I really want the API to always act as described in the documentation. We wouldn't be bothering to produce a printed manual otherwise.

 

This design was adopted so that in the future it can be used for plugins. A plugin would add its own hooks onto objects. If we used SetHook(), it implies there is only a single hook allowed. We want the user to be able to add their own hooks without overriding any plugins they may be using. This also allows multiple plugins to add and remove their own hooks without interfering with one another.

 

It's too early to worry much about a plugin system. The best thing to do right now is build a really focused tool that fulfills the function it's designed for. However, by anticipating plans for future expansion we can design things now so that we don't have to go back and change them later.

  • Upvote 1

40 Comments


Recommended Comments



L B

Posted

Haha, I agree with Josh. Don't use your own custom, globally unknown and confusing type defs.

 

If you feel like "float", "int", "unsigned char", etc. are not specific enough and platform variant, use stdint.h (which is, you know, explicit, standardized and known by most C/++ programmers).

 

int => int32_t
unsigned short => uint16_t
unsigned char => uint8_t

 

etc.

 

http://stackoverflow...e-or-not-stdint

Road Kill Kenny

Posted

I can't stand typedefs like that. I don't see why its necessary to typdef variable types that are already short. Its a little more understandable when they get longer.

Canardia

Posted

One big benefit is that you can do object specific typedefs, like first using a an int for some id, and later needing a long long because you got more players.

Roland

Posted

Here is the way I do things. I don't mean to lecture or something like that. Just my view of this, Good or Bad wink.png

 

when argument is a simple data type that won't be changed by called function - use int arg, float arg etc...

 

when argument is a complex data type that won't be changed by called function - use const TVec3& arg, const std::string& arg etc

 

when argument is a simple data type that will be changed by called function - use int& arg, float& arg

 

when argument is a complex data type that will be changed by called function - use TVec3& arg, std::string& arg

 

Depending of situation and implementation you can use pointers (*) instead of references (&)

 

If a function does not change any internal state (variable) of the class is should be declared const, like void myClass::isEverytingOK( ) const;

 

I strongly recommend to avoid to much typedef's. Use them with care like in

typedef std::map<std::string,myObect*> ObjectMap;

 

I strongly recommend to avoid macros and defines. Most time const static's, enums or inline functions can be used instead. This make things typed. And no "typedef int Speed;" or any such silly things. An int is an int :)

 

Furthermore in my experience (I'm no speed guru by anyway) the devil lies in nested inner loops and loading/saving. But in the projects I have done (99% non game related) the biggest time thief has ALWAYS been the program design.

 

Those are my humble thoughts that I live by. Surely not the truth or even the best thing but so far i found it best for me. Maybe for you also. If not, just ignore this post biggrin.png

  • Upvote 1
Furbolg

Posted

I agree full to Roland, except this one:

Use them with care like in

typedef std::map<std::string,myObect*> ObjectMap;

 

But thats only a opinion thing, if you can work with it its fine smile.png

  • Upvote 1
Canardia

Posted

I guess it's just a question of taste and elegancy. Most people don't do things very elegant, because their taste is stronger than elegancy :)

  • Upvote 1
paramecij

Posted

Yes, it should be universal, it should be int8, int16, int32, int64, float16, float64... in every programming language period. then algorithms and other code would be more easily portable. among other things.. and it's ONLY two types of number anyway, fixed point and floating point. But in real world it seems there is more types then there is known species on this planet...the type gets resolved at compile time anyway, so it's there just for our pretending...

  • Upvote 1
Shaping

Posted

Josh, is the C API being developed concurrently with the C++ and Lua?

Josh

Posted

Josh, is the C API being developed concurrently with the C++ and Lua?

We wrote a little tool that scans the header files and generates the ToLua++ clean header file, which gets compiled into the Lua glue code. So the Lua API always matches the C++ API 100%.

Shaping

Posted

We wrote a little tool that scans the header files and generates the ToLua++ clean header file, which gets compiled into the Lua glue code. So the Lua API always matches the C++ API 100%.

 

I need (and thought from the early discussions that you intended to provide) a lowest-level function-based and call-back-based C-API implemented as a DLL, with the corresponding .h file. I don't see how that relates to Lua. The C API should resemble the C++ class member functions with perhaps some extra prepended syntax to scope and associate concepts correctly. Will such a DLL happen? I was counting on it.

 

Also, can you explain the "ToLua++ clean header file" , the glue code, how the two are related, and where the DLL fits into all of this? My dev eviron can link to a C API in a DLL via STDCALL, CDECL, and FASTCALL calling conventions. The dev environ also supports callbacks from the DLL. This is fairly typical of any C FFI for a dynamic programming environment.

 

So are we talking about Lua in a DLL?

Josh

Posted

I need (and thought from the early discussions that you intended to provide) a lowest-level function-based and call-back-based C-API implemented as a DLL, with the corresponding .h file. I don't see how that relates to Lua. The C API should resemble the C++ class member functions with perhaps some extra prepended syntax to scope and associate concepts correctly. Will such a DLL happen? I was counting on it.

 

Also, can you explain the "ToLua++ clean header file" , the glue code, how the two are related, and where the DLL fits into all of this? My dev eviron can link to a C API in a DLL via STDCALL, CDECL, and FASTCALL calling conventions. The dev environ also supports callbacks from the DLL. This is fairly typical of any C FFI for a dynamic programming environment.

 

So are we talking about Lua in a DLL?

Oh, I didn't read that closely. No, we have one object-oriented API. I've got to focus on that right now or we'll never get Leadwerks 3 out. Then we can consider a full procedural one.

Guest Red Ocktober

Posted

good insights Roland, Furblog & Rick...

  • Upvote 2
Shaping

Posted

Oh, I didn't read that closely. No, we have one object-oriented API. I've got to focus on that right now or we'll never get Leadwerks 3 out. Then we can consider a full procedural one.

 

Is this a mostly straightforward, possibly somewhat automated conversion process? Any idea how long after the release of the OO Leadwerks framework we will see the purely functional equivalent in a DLL?

Josh

Posted

I can't say because we are tight for time with the GDC coming up. After then it becomes more feasible.

 

The best way to do this would be to just pull the syntax straight from the docs, and then you could auto-generate 98% of this.

  • Upvote 1
Shaping

Posted

Right---that's what I was thinking. I'm standing by to test this whenever it is ready...


Guest
Add a comment...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...