views:

96

answers:

5

I'm developing a GUI library with a friend and we faced the problem of how to determine whether a certain element should be clickable or not (Or movable, or etc.).

We decided to just check if a function exists for a specific object, all gui elements are stored in a vector with pointers to the base class.

So for example if I have

class Base {};
class Derived : public Base
{
    void example() {}
}
vector<Base*> objects;

How would I check if a member of objects has a function named example.

If this isn't possible than what would be a different way to implement optional behaviour like clicking and alike.

A: 

I'm not sure it is easy or good to do this by reflection. I think a better way would be to have an interface (somethign like GUIElement) that has a isClickable function. Make your elements implement the interface, and then the ones that are clickable will return true in their implementation of the function. All others will of course return false. When you want to know if something's clickable, just call it's isClickable function. This way you can at runtime change elements from being clickable to non-clickable - if that makes sense in your context.

FrustratedWithFormsDesigner
+1  A: 

I would create an interface, make the method(s) part of the interface, and then implement that Interface on any class that should have the functionality.

That would make the most sense when trying to determine if an Object implements some set of functionality (rather than checking for the method name):

class IMoveable
{
    public:
        virtual ~IMoveable() {}
        virtual void Move() = 0;
};

class Base {};

class Derived : public Base, public IMoveable
{
    public:
        virtual void Move()
        {
            // Implementation
        }
}

Now you're no longer checking for method names, but casting to the IMoveable type and calling Move().

Justin Niessner
+3  A: 

The best way to do this is to use mixin multiple inheritance, a.k.a. interfaces.

class HasExample // note no superclass here!
{
    virtual void example() = 0;
};

class Derived : public Base, public HasExample
{
    void example()
    {
        printf("example!\n");
    }
}

vector<Base*> objects;
objects.push_back(new Derived());

Base* p = objects[0];
HasExample* he = dynamic_cast<HasExample*>(p);
if (he)
    he->example();

dynamic_class<>() does a test at runtime whether a given object implements HasExample, and returns either a HasExample* or NULL. However, if you find yourself using HasExample* it's usually a sign you need to rethink your design.

Beware! When using multiple inheritance like this, then (HasExample*)ptr != ptr. Casting a pointer to one of its parents might cause the value of the pointer to change. This is perfectly normal, and inside the method this will be what you expect, but it can cause problems if you're not aware of it.

Edit: Added example of dynamic_cast<>(), because the syntax is weird.

David Given
If you're using `dynamic_cast<>`, remember to turn RTTI on. (Some people use the `-fno-rtti` flag out of habit or coding requirement.)
Mike DeSimone
+1  A: 

If you're willing to use RTTI . . .

Instead of checking class names, you should create Clickable, Movable, etc classes. Then you can use a dynamic_cast to see if the various elements implement the interface that you are interested in.

IBM has a brief example program illustrating dynamic_cast here.

dsolimano
+7  A: 

You could just have a virtual IsClickable() method in your base class:

class Widget {
public:
    virtual bool IsClickable(void) { return false; }
};
class ClickableWidget : public Widget
{
public:
    virtual bool IsClickable(void) { return true; }
}
class SometimesClickableWidget : public Widget
{
public:
    virtual bool IsClickable(void); 
    // More complex logic punted to .cc file.
}
vector<Base*> objects;

This way, objects default to not being clickable. A clickable object either overrides IsClickable() or subclasses ClickableWidget instead of Widget. No fancy metaprogramming needed.

EDIT: To determine if something is clickable:

if(object->IsClickable()) {
   // Hey, it's clickable!
}
Mike DeSimone
Much better approach.
David Thornley
So if IsClickable is true, than what do I do, don't I need to typecast and call the function or something.
Xeross
If I understand correctly I need to define every function the children might have (click, drag, etc.)
Xeross
I was answering the question "how to determine whether a certain element should be clickable or not (Or movable, or etc.).", and yes, you would need to define in `Base` every function you would want to call on a `Base*`. The alternative is using RTTI (see David Given's answer), but that route requires you to define a *class* for every function you might want. Or you could use RTTI to determine if a `Base*` is really a `Clickable*` before calling `IsClickable()`; then `IsClickable()` would only need to be in `Clickable` and its subclasses.
Mike DeSimone
As an aside, since you're doing GUI code, I'd suggest you look at the [libsigc++](http://libsigc.sourceforge.net/) library for event handling.
Mike DeSimone
We went with your method but instead of using an isClickable function we're using a variable that contains all possibilities as flags. And we already have a library to handle the events, was built into the graphics library we're using.
Xeross
Ah, so you will "push" clickability (i.e. anything that could change "clickable" will need to do it) rather than "pull" (as above, clickability is determined on demand). As for flags, it will still help your code's readability if you define accessor functions, e.g. `bool IsClickable(void) { return flags }` and `void SetClickable(bool clickable=true) { if(clickable) flags |= CLICKABLE_MASK; else flags if(IsVisible()) Redraw(); }` (the latter should be in the .cc file; it's a bit much to inline).
Mike DeSimone
Continuing the last note: you might also want to have `SetClickable()` check that clickability is changing before doing a redraw.
Mike DeSimone
Incidentally, a cunning trick when using flags is to do 'bool myflag : 1;' when declaring your member variables. This uses the little-known bitfield feature to produce variables that occupy a single bit. Most compilers will pack multiple such flags into a single word, so giving you equivalent functionality to using shifts and masks but without actually having to write code.
David Given
@David: It's a little-known feature because how the bit fields are packed into the actual structure is entirely compiler-dependent. The Linux kernel uses this technique, but has to define several platform-dependent compile-time flags to make sure things get put in the right places. In general, if it's only used internally in your code, does not cross platforms, does not represent hardware registers, and is never put into persistent form (i.e. written to a file or socket), then bit fields can be useful. But for most people, it's too much risk.
Mike DeSimone
@Mike: Risk? What risk? *Any* data structure is platform-independent! Pretty much by definition. Bitfields are defined in the spec which means they have to work. The worst implementation I've seen is on early Visual C compilers which stored each bitfield element in a machine word, but that's no worse than using full bool instance variables, and even then it's vastly more robust than storing flags in an int using bit shuffling --- no more risk of forgetting that ~ when masking out bits, for example. And if you're trying to push structures down the wire, well, you've got worse problems.
David Given
For example, if you create a structure containing data as 32-bit integers on a little-endian platform, then transmit that structure as a stream of bytes to a computer with a big-endian ordering, then the receiving computer must manually detect the endianess reversal and compensate. C doesn't do it for you, so if you forget, you get undefined behavior. *That* risk.
Mike DeSimone
In the case of bit fields, the compiler can order the fields from most-significant-bits (MSBs) to least-significant bits (LSBs), or LSBs to MSBs, may or may not pad between the fields, and may place padding on the MSB or LSB end of the parent type (short, int, long, whatever), and still remain in compliance with the C specification. These are the behaviors that Linux has to define flags for, and in the case of using bitfields with hardware registers, each struct has to be defined four times to account for all these combinations. (IIRC, the no-padding case doesn't appear in practice.)
Mike DeSimone