Jump to content
  • entries
    943
  • comments
    5,899
  • views
    924,389

One Little Thing


Josh

8,399 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



Hi Josh, really nice to hear this.

 

One question: why the addhook function has a reference for the first parameter ? (if you use native datatypes (int, float ,char etc) it dont make sense to use reference/pointer because the variable/parameter which stores the adress also consumes memory. So you should use call by value in this case (int hookid))

Link to comment

Also if I recall correctly the memory size of a pointer / reference is the same size as a float and bigger than an int so there is no real gain in using a reference / pointer with these native types.

  • Upvote 1
Link to comment

It isnt about the size (in times of giga bytes), its about speed (lumooja) but more about good practise. No serious c++ programmer (and you advertise LE3 as c++ engine) will use reference for an simple int.

 

Its like having spelling errors on your website, it looks unprofessional... ok it dont matter in this case.

 

Just wanted to give a tip.

Link to comment

Ok I'm not "bikshedding" here I'm just asking out of personal interest. If you are copying the argument... wouldn't it be quicker to copy something of a smaller size.. It just seems logical that it would be faster to copy something smaller in memory than something bigger.

 

And yes I do realize that this isn't the only thing relating to speed but I'm just wondering about this aspect specifically.

Link to comment

Can we have an extra pointer also so we can pass around 'this' when we call this from within a class? I hate all these C callback functions when the library is C++. The way it is pretty much requires globals if we want to use anything in these hooks other than the entity in question, which we most likely will, and forcing me to use globals to get anything inside these hook functions just sucks and in my view is bad practice.

 

Any C callbacks you have should allow the extra pointer like LE 2 has to allow us to pass things around instead of making anything global.

Link to comment

I dont know much about the mobil devices but maybe std::function / std::bind is a solution ? (requires c++11 / visual studio 2012 afaik)

Link to comment

You can do this with "normal" C++ and any recent VS, and bind with class methods with templates, but I don't think that'll happen.

Link to comment

got you on that Carnadian. For long I had both VS and CodelBlocks.. Now its only CodeBlocks 100%. He! I even uninstalled VS smile.png as I found my self very comfortable with the simplicity of CodeBlocks

Link to comment

Everyone has his favorite tools wink.png (and i cant work without visual assist or resharper anymore *vanish* :))

 

@Rick: yes, it was just an idea because everything in std:: (except tr1 etc.) is standard c++ and the chances it works on other (modern/actual) c++ compiler are pretty high.

Link to comment

@Furbolg Like templates smile.png Honestly if Josh would download the event code I posted in the asset store this would be a slam dunk and take mins to implement, but people get all freaked out and think it's to complicated...

Link to comment

**********FREAKING OUT**********

I think it's too complicated. ohmy.png

 

Roland and Stack Overflow have confirmed what Furblog was saying about references and floats, ints, etc. Thank god for full project search and replace.

 

On the docs, rather than editing 600 entries I'm just going to write some PHP that replaces "const float& " with "float", etc.

 

Apparently there is no clear advantage to using const in front of floats either. Well, it depends on the compiler, the phase of the moon, etc. There are lots of situations when it is convenient to modify a float parameter though, so not using const in front of it does give an advantage in terms of flexibility.

 

Of course for things like a Vec3, there is a clear advantage to using const Vec3& because it avoid copying the larger contents of the Vec3 object.

 

I learn something new every day.

  • Upvote 2
Link to comment

You should have used own types for everything, like:

typedef const float& constfloatref;

That's what basically every C++ library does too.

 

However, int& and float& are still better than copying the value, because if your original value changes, it is also reflected in the function. By copying you have to manually set the new value to each function which copied it.

Link to comment

That's why I suggested also to use cpufloat and gpufloat typedefs, so you can use maximum accuracy and speed where it's possible: cpufloat would be double, and gpufloat would be float by default, but you could change it to double when 64-bit gpus come out.

Link to comment

You should have used own types for everything, like:

typedef const float& constfloatref;

That's what basically every C++ library does too.

 

Thats weird naming and typing.

What i personally do is something like this

// types.h
typedef signed char s8; // 1 –128 to 127
typedef signed short s16; // 2 –32,768 to 32,767
typedef signed long s32; // 4 –2,147,483,648 to 2,147,483,647
typedef signed long long s64; // 8 –9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
typedef unsigned char u8; // 1 0 to 255
typedef unsigned short u16; // 2 0 to 65,535
typedef unsigned long u32; // 4 0 to 4,294,967,295
typedef unsigned long long u64; // 8 –9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
typedef float f32; // 4 3.4E +/- 38 (7 digits)
typedef double f64; // 8 1.7E +/- 308 (15 digits)

 

However, int& and float& are still better than copying the value, because if your original value changes, it is also reflected in the function. By copying you have to manually set the new value to each function which copied it.

 

1. this isnt true... copying primitive/nativ types is faster

2. You have to watch the side effects when you using reference/pointers they can change anytime (especially pointers, the can pointing into invalid memory etc.)

 

Lets say you have a function which sets a box position with a simple float reference, now you create another box and use the same variable to place the box. What happens now is that both boxes are at the same place.

(You cant use literals for reference, a reference need a valid object at the time you are using it also null/nullptr is not allowed)

Link to comment

You should have used own types for everything, like:

typedef const float& constfloatref;

That's what basically every C++ library does too.

...

 

F'n yeah! And don't forget to brand them also, like LE3constfloatref... you know so they're easily distinguishable between other, lesser, more primitive types ;):D

Link to comment

Why should it even be a float? That indicates a small-minded commitment to that a variable is a number. Instead it should be 'LeadwerksDataTypeConstVariableValueRefRealNumberFloat'.

 

But why restrict it to just Leadwerks? Let's make a data type everyone can use:

'UniversalDataTypeSystemDataTypeConstVariableValueRefRealNumberThat HappensToUseFloatingPoints'

  • Upvote 5
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...