Perl style scalar type in C++?

Started by
22 comments, last by Deyja 19 years, 1 month ago
in my game, various components need to signal each other by using a signal method

   void Signal(const string &id , Signal sig);

now the signal should be able to hold various different data types, in a stack (or a hash, would be equally as good).

    int
    int
    char
    string
I've only come up with 2 solutions. 1 is using ints and casting everything to ints (anything larger, is cast as a pointer - which is deleted afterwards !), or allocating memory using new and sizeof (again pointers, and void data type). What are you thoughts/suggestions/warnings? [Edited by - Genjix on March 6, 2005 5:37:16 PM]
Advertisement
Why not just have different types of signals subclass Signal? better yet, why not have different per-signal methods? Best of all, why not use a premade signal/slot framework such as that provided by Boost?
I won't go into the specific details, but having different subclasses for each signal wouldn't be appropriate.
        Signal sig;        sig.Int(10);        sig.Str("Hello");        sig.Ptr((void*)&obj);

What would you do when a certain function needs to parse the structure?
void A::Foo(Signal s){     //... if i want to interpret     // values in s, how do i this?     // I still, can only access members     // that Signal has.}

Eventhough boost's libraries are good, its signal/slot implementation doesn't suit what I'm doing. I do appreciate your help though.

[Edited by - Genjix on March 3, 2005 3:20:20 PM]
Off the top of my head something this simple should do the job. Ofcourse it won't work for types you don't want to copy & you'll have to grab the thing by reference in your function (& not call the function the same name as the class :P), but you should really write a copy constructor for it anyway to handle such things.
struct Signal(){  /**/ Signal(char &value)    value(&new char(value)),    type(type_char)  {}  /**/ Signal(int &value)    value(&new int(value)),    type(type_int)  {}  /**/ Signal(string &value)    value(&new string(value)),    type(type_string)  {}  /**/ ~Signal(){delete value;}  void* value;  enum{    type_int,    type_char,    type_string  }type;};
_______________________________ ________ _____ ___ __ _`By offloading cognitive load to the computer, programmers are able to design more elegant systems' - Unununium OS regarding Python
Quote:Original post by Genjix
//... if i want to interpret
// values in s, how do i this?
// I still, can only access members
// that Signal has.
By using RTTI. Of course, this can be ugly (just as variant types like you want can be ugly), which is why a premade signal/slot library is useful.

In what way is the boost signal/slot library insufficient for your needs?
Else there's Boost::Any

Hope this helps.
#ifndef NODE_H#define NODE_H#include "signal.h"class World;/**Base abstract 'Node' framework classThis class is the class inherited by each node.Node's are different part's of the application,and can be thought of as little clients.Signals are recieved and sent as strings.*/class Node{    public:	/**update class pointer to parent*/	Node(World *par);	/***/	~Node();	/**Recieve and Interpret a signal		RecieveSignal should be overriden, by all derived	node classes*/	virtual void RecieveSignal(const string &id , Signal &sig);    protected:	/**Send a signal to parent to broadcast		Broadcast should not be overriden as it is used	to communicate with the other nodes, via the parent*/	void Broadcast(const string &id , Signal &sig);	/**Send a signal to parent to relay		SendSignal should not be overriden as it is used	to communicate with the other nodes, via the parent*/	void SendSignal(const string &name , const string &id , Signal &sig);    private:	World *parent;		///< Node's Parent};#endif

All the different nodes, are created, and registered with the app. Different nodes need to communicate, and parameters need to be passed around.

As for a new data type for each different type of signal... well the reciever and sender BOTH need to know the signal type (so reciever can cast back to type to read data), and since there are potentially lots of signals, and a broadcast...

ProPuke's solution is the simplest, and my favorite... but what happens with lots of data types, use pointers? I might as well cast everything to ints, and anything else read the pointer and cast as an int.

boost::any is exactly what i need (especially type checking)... but i need to link against boost, and its a bit OTT for whats needed. What would be better :D lmao, would be if anyone could tell me how its is able to accept any type, and how it internally stores the typeinfo.

If it helps, the system im (trying) to design is more like Qt's mechanism.

Thank you for your time.

this is what I've decided to go for (I still want your ideas :) )

main.cpp

#include "world/world.h"#include "node/testnode.h"#include "event/eventinterpreter.h"int main(){	World myworld;	TestNode genericnode(&myworld);	myworld.Register("GenericNode",genericnode);	/*testing testing*/	Signal soh("this is message");	soh.params["age"] = 10;	soh.params["name"] = (int)"Amir Taaki";	myworld.Broadcast("hello",soh);	myworld.SendSignal("GenericNode","bye",soh);	/*tie our World to eventinterpreter*/	EventInterpreter ei(myworld,"amir.lua");	return 0;}

signal.h
#ifndef SIGNAL_H#define SIGNAL_H#include "types/stlext.h"#include <string>using namespace std;/**not yet implemented*/class Signal{    public:	/***/	Signal(const string &d);	/***/	~Signal();	map<string , int> params;///< signal parameters (cast to ints)	const string debug;	///< debug message};#endif

and I unroll the signal
cout << (const char*)sig.params["name"] << " is "	<< sig.params["age"] << "\n";

normally I wouldn't ever dream of doing something as simple as this :P

Eww. What you really want is a discriminated union.

"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
You can store type info using polymorphism.
class BType{virtual ~BType() {}};template<typename T> class DType{public:virtual ~DType() {}T the_object;/* appropriate constructors... */};


In your 'any' class, you have a templated set function that creates an object of type DType<T>. You store this using a pointer to BType. Later, if you want to check to see if it's of type A, you attempt a dynamic_cast<DType<A>*>(ptr_to_BType). If it returns non-null, you've got a value object of DType<A> and can access the_object. If it returns null, it's not the right type.

I've got a non-boost variant class that operates this way. Let me slice it out of my lib and I'll pass it along. :D

This topic is closed to new replies.

Advertisement