Jump to content
  • entries
    945
  • comments
    5,899
  • views
    930,190

One Little Thing


Josh

8,502 views

 Share

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
 Share

40 Comments


Recommended Comments



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

Link to comment

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.

Link to comment

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.

Link to comment

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
Link to comment

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
Link to comment

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
Link to comment

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
Link to comment

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%.

Link to comment

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?

Link to comment

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.

Link to comment
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?

Link to comment

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
Link to comment

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...