Sign in to follow this  
Maverick Programmer

Component Oriented Programming in C++ ( COP ) ?

Recommended Posts

Hello, A friend of mine mentioned a type of programming called COP or Component Oriented Programming. In C++, I typically use OOP can never heard of COP. My friend described COP as something along the lines of this His quote: "Well, sorta. COP uses these but the main idea is something like this (just an example, it won't necessarily look like this):"
CEntity *pPlayer = CEntityManager->AddEntity("Player"); // just an entity with name "Player"

 

pPlayer->AddComponent(CComponentFactory->CreatComponent("Position")); // player now has position (x, y, z)
pPlayer->AddComponent(CComponentFactory->CreatComponent("3DModel")); // player now has a mesh that can be shown on screen
pPlayer->AddComponent(CComponentFactory->CreatComponent("Body")); // player now has body for collision
pPlayer->AddComponent(CComponentFactory->CreatComponent("Run")); // player now can run (has speed and acceleration, etc...)
pPlayer->AddComponent(CComponentFactory->CreatComponent("Jump")); // player now can jump
pPlayer->AddComponent(CComponentFactory->CreatComponent("Attack")); // player now can attack
pPlayer->AddComponent(CComponentFactory->CreatComponent("Storage")); // player now can carry items
pPlayer->AddComponent(CComponentFactory->CreatComponent("Lock-On")); // player now can lock on to targets

pPlayer->AddComponent(CComponentFactory->CreatComponent("A.I.")); // player now can think (or for enemy)

pPlayer->AddComponent(CComponentFactory->CreatComponent("Input")); // player now can be controlled through keyboard and mouse

 

pPlayer->GetComponent("Position")->SetPosition(float x, float y, float z);
pPlayer->GetComponent("3DModel")->SetMesh(CMeshManager->LoadMesh("mesh.3ds"));

pPlayer->GetComponent("A.I.")->SetBehavior("FollowTarget", "NPC00125");

 
After this quote: "And so on and so forth :P As you can see with this system you can create any player, enemy, items, NPCs, and whatever and they can have any attributes and functions depending on the components they have (a tree that can talk, a flying sword that attacks, etc...). The game designers can go crazy with their ideas without having to worry about having a class for each type of entity they want to make (no more CPlayer, CEnemy, CItem, etc...)" So my question is this: how do you go about making something like this? Any kick-start answers or links to tutorials would be very helpful. Thank you for your time! ~Maverick

Share this post


Link to post
Share on other sites
I think this would be pretty easily implementable in C++ (and it's been done in Bay 12 Games's Dwarf Fortress). Create an extensible container for each object which contains some form of mnemonic token. At some point during run-time, your program reads through the tokens and performs whichever actions you want on it. The details, especially when and how you want the program to check for tokens, depends on what you need your software to do.

Share this post


Link to post
Share on other sites
Check out my project for an idea on how this can be achieved. My approach is container based wherein entities are containers of components. In my system, entities assimilate or expose aspects of their underlying components, where aspects include attributes, events, and operations.

I haven't moved code into the repository yet, so the wiki will have to do. Sorry.

As mentioned on the linked page, I used Boost.Any and Boost.Function to help achieve my design. You may want to look into those.

Others here have started threads with their (more expert) thoughts and designs. I'm sure someone can provide links.

Hope this helps!

EDIT:

I also used Boost.Signals. You can see the correlation between these libraries and the three types of aspects I mentioned.

Share this post


Link to post
Share on other sites
That sounds like some fancy words for something which is basically just dynamic objects. It's a good approach.

Steps to implementing dynamic objects:

1) Every "object" has a hashtable. These are what your friend calls "entities".

2) Every element in the hashtable is an association from a string (its name) to some variant structure that contains the relevant data. These elements are what your friend calls "components"

To implement variant types, you can use boost::any or you can roll out your own system.

Share this post


Link to post
Share on other sites
It would be nice if you could just do what is done in the example, but tell me, how are you doing this:

pPlayer->GetComponent("Position")->SetPosition(float x, float y, float z);
pPlayer->GetComponent("3DModel")->SetMesh(CMeshManager->LoadMesh("mesh.3ds"));

pPlayer->GetComponent("A.I.")->SetBehavior("FollowTarget", "NPC00125");




without a typecast?

GetComponent() would have to return a type that has SetPosition(), SetMesh() , SetBehavior(), etc. as member functions. What's the use of those components if you need all the functionality in that type (returned by GetComponent())?

I've found this document. In the sample code of the document an "Object"(in Java) is returned, which also needs to be casted.



[Edited by - delta user on July 3, 2008 5:22:52 PM]

Share this post


Link to post
Share on other sites
Quote:
Original post by Zahlman
He's making terms up. All that he's doing is object composition, which is a very, very common idea in OOP.


Forgive me if I'm wrong, but I think there is a slight difference. Object composition is defined at compile time and Component oriented allows you to add components to entities at run time (Hence the "AddComponent" in the original code snippet. I think it's meant to lead to greater flexibility.)

Quote:
Original post by delta user
without a typecast?

As far as I see you are right. If there is only one set function per component you could just have the function accept a boost::any.

Share this post


Link to post
Share on other sites
Good god, I don't even want to imagine how incredibly inefficient doing something like that in runtime would be using C++. Doing a string hash lookup (or even multiple ones) for every single operation on every single component of every single object, every single frame. Yuck. This really screams premature un-optimization...

If you want to use such a model with string indices, then use a language that supports that natively (and pre-hashes the strings in your source, for example). Some scripting languages come to mind.

Share this post


Link to post
Share on other sites
This sound suspiciously much like dynamic typing (modulo duck typing). You throw away all kinds of static type safety:

- no checking if a name like "Jump" exists
- no knowlegde of the type of a component (other than "it's some kind of component")

and this in turn enforces:

- downcasting or equivalent (with dynamic errors for the static class hierarchy... eek! not even duck typing!)

The three usage examples you posted would need a dynamic downcast, thus effectively locking their type to a static one, but only enforcing this at run time. I can't imagine why your friend wants to do this.

I am a big fan of static composition, but dynamic composition in a statically typed language?

Maybe I'm missing something?



[Edited by - Ahnfelt on July 3, 2008 7:19:55 PM]

Share this post


Link to post
Share on other sites
Quote:

This sound suspiciously much like dynamic typing (modulo duck typing). You throw away all kinds of static type safety:

- no checking if a name like "Jump" exists
- no knowlegde of the type of a component (other than "it's some kind of component")

and this in turn enforces:

- downcasting or equivalent (with dynamic errors for the static class hierarchy... eek! not even duck typing!)

The three usage examples you posted would need a dynamic downcast, thus effectively locking their type to a static one, but only enforcing this at run time. I can't imagine why your friend wants to do this.

I am a big fan of static composition, but dynamic composition in a statically typed language?

Maybe I'm missing something?


According to one of the articles I read (provided in this topic), it mentioned that this was more flexible. I honestly do not see why using this method would be such a problem. The entity would be made up of the smaller components in which the component manager would take care them in return. Also, what limits me to checking if "Jump" does exist? All one would have to do is compare the string to a list of component names, if none exist, direct that new component to a null one and throw the error/warning.

Maybe I'm missing the important reason why not to use this method?
(Also, its' kind of fun to try and figure out. ;p )

~Maverick

Share this post


Link to post
Share on other sites
I agree with Ahnfelt.
You need to downcast the components to a basic type.

It is actually nonsense to do something like this:

pPlayer->GetComponent("A.I.")->SetBehavior("FollowTarget", "NPC00125");

you could just as well do this:

pPlayer->GetAIComponent()->SetBehavior("FollowTarget", "NPC00125");

because at that line of code you must get the AI component (SetBehavior() only exists for the AI component), even if you would cast it, or else it would give undefined behavior.

If you do want to make it dynamic you need a function that every component has inherited.
Maybe something like this:


ComponentAction action= new ComponentAction();
action->SetAction("SetBehavior");
action->AddActionArgument( new ComponentActionArgument("FollowTarget") );
action->AddActionArgument( new ComponentActionArgument("NPC00125") );

pPlayer->GetComponent("A.I.")->CallComponentAction(action);



Where you use a parser to get the action, the arguments and the object on which you want to perform the action.

I hope this is somehow usefull.

Share this post


Link to post
Share on other sites
It's always possible to rewrite things as:
setBehavior(object["AI"], "follow", "obj1337");


Free functions solve open-closed issues more easily. As a side note, it would be better to write things as:
object[AI].setBehavior("follow", "obj1337");


Since here, AI is an object, its type can contain information about the returned type, and thus allow such manipulation without any casting (either up or down).

Share this post


Link to post
Share on other sites
Ah, I think I get it now. I was thinking of how it would be to use this system for composing objects in code. But it's purpose is to be able to dynamically compose objects from a tool (or data). The benefits are clear then.

Share this post


Link to post
Share on other sites
It seems what you want is to have dynamic duck-typing, ala Python and co.
That's perfectly doable safely without any cast. Just make the type of your objects be (ML-like syntax):

type variable = Object of object | Function of (variable list -> variable) | Procedure of (variable list -> unit)
| Int of int | Float of float | Array of (variable, variable) map | Resource of int
and
type object = (string, variable) map

A variable is either an object, an int, a float, an array, a resource, a function or a procedure.
An object is a map of strings to variables.

There you go, you have it.

Share this post


Link to post
Share on other sites
Quote:
Original post by loufoque
It seems what you want is to have dynamic duck-typing, ala Python and co.
That's perfectly doable safely without any cast. Just make the type of your objects be (ML-like syntax):


This prevents extension: you cannot grab a value from an third-party library (such as a DirectX texture) and make it a member of your object. Also, from a language semantics point of view, pattern-matching is the ML equivalent of dynamic_cast.

Of course, there are ways of doing this in a type-safe manner. Consider the following module definition (in ML terms) :

type 'a property

type obj =
{
set : 'a. 'a property -> 'a -> 'a;
get : 'a. 'a property -> 'a;
}

val make_property : unit -> 'a property
val make_object : unit -> obj


This can be implemented entirely in OCaml without applying Obj.magic (though a little C hacking may be necessary to have it play nice with the garbage collector).

Share this post


Link to post
Share on other sites
Given a union type in ml or ocaml - how effecient is the implementation of the pattern matching? would a set of alternative pattern matches for different type constructors be o(1) (ie like a switch statement in c/c++ ) or would it be o( n) (ie if you had to exhaustively test each type with dynamic_cast in c++ without an enum type system). i am assuming no additional predicate guards or other complicated pattern matching constructs.

component orientation leads naturally to a subsystems approach (the subsystem works with the component type and knows the components type) and other parts of the code do not directly access the components of an entity in an arbitrary fashion. the collection of hetrogeneous components then doesnt have to have general component accessors on it that would require slow and type unsafe duck typing or type casting.

Share this post


Link to post
Share on other sites
Quote:
Original post by Yann L
Good god, I don't even want to imagine how incredibly inefficient doing something like that in runtime would be using C++. Doing a string hash lookup (or even multiple ones) for every single operation on every single component of every single object, every single frame. Yuck. This really screams premature un-optimization...


That is what Mick West calls 'object as a component container.' What component oriented design is trying to achieve is 'object as pure aggregation.' Each object is purely an identifier, when components are added corresponding component manager adds a reference to the object (the object does not keep a list of components.)

So

object -> add_component ( renderer_component )

would not chance the object, but would register the object to the renderer component manager. As each manager contains a list of the entities, its simply a list traversal to perform their frame by frame operation.

Share this post


Link to post
Share on other sites
It's a largely unimportant and uninteresting distinction though. Either way you have components that can be logically grouped by entity or by function. It's just a case of how you represent it with the language.

Share this post


Link to post
Share on other sites
Quote:
Original post by Kylotan
It's a largely unimportant and uninteresting distinction though. Either way you have components that can be logically grouped by entity or by function. It's just a case of how you represent it with the language.


It's my understanding that each has a different level of indirection in getting to the entity data though, which could be considered an important difference. As Yann L pointed out, grouping by entity requires an extra hash look up every operation, which can pile up quite quickly (extra lookup per operation per entity per frame.) Grouping by function avoids this overhead.

Share this post


Link to post
Share on other sites
It's only significant overhead if the indirection is slow. Typically when you group things by entity then you have direct pointers to the objects in question so it's not a big deal. Grouping by function might be slightly quicker when performing the batched operations, but you lose performance when it comes to the interaction between the components. It's all much the same really.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this