EDIT: Total Overhaul
I am making a programming language (similar to Python), and at least for now, I am trying to compile it to C++. I'm having an error where C++ is saying that there is no member "value" in the class "Object". This is a compilation error. I understand why C++ is doing this, as it has to know the type of value at compile-time, and so I am asking for alternatives.
The only things it will be given are instances subclasses of Object, in which the member "value" will be defined, this is a compile error.
I can't declare the member "value" inside the Object class, because its type depends on which subclass it's in. This also has to work with floats and strings, so defining the function with an Integer instance instead of an Object instance won't work.
Here is some code with an example of how these objects will be used:
auto user_i = std::make_unique<Integer>(2);
std::cout << (*user_i).equals(*std::make_unique<Float>(2.0).get()) << std::endl;
Here are the important class definitions:
class Object {
public:
bool equals(Object& other) {
throw "Not implemented!";
}
};
class Integer: public Object {
public:
int value;
Integer(int val) {
value = val;
}
bool equals(Object& other) {
return value == other.value;
}
};
class Float: public Object {
public:
double value;
Float(double val) {
value = val;
}
bool equals(Object& other) {
return value == other.value;
}
};
class String: public Object {
public:
string value;
String(string val) {
value = val;
}
bool equals(Object& other) {
return value.compare(other.value) == 0;
}
};
It should not be the case that 2 == "2", but it should be the case that 2 == 2.0 == 2.000 etc.
There is then the added complication of methods like add (by this I mean returning a value, still not modifying the object). The equals method suggested by #Michael Karcher would work fine, but for example 2+3 should return 5, not 5.00000.
For even more trouble, User-Defined Objects (Classs in my language) should be able to override methods like add.
Your example has a couple of issues besides you main issues. I start with the minor issues to get them out of the way.
You need declare equals as virtual in the base class. Opposed to Java, dynamic dispatch is not the default in C++, but must be requested per method using the virtual attribute like this:
class Object {
public:
virtual bool equals(Object other) {
throw "Not implemented";
}
}
You are passing the object you compare to by value. Opposed to Java, in C++ even class types can be passed by value. This means that the function equals gets a copy of an Object to compare to - and just that. The parts of the object you want to compare to, including the value member, do not get copied into the argument passed to equals. You should pass the parameter by reference. As your equals function does not need to modify the object you compare to, a reference without write permission (commonly called a const reference due to the syntax) is enough:
class Object {
public:
virtual bool equals(const Object& other) {
throw "Not implemented";
}
}
If you are writing a base class that just provides a function signature which needs to be overridden in every derived class, you don't make it throw something, but you instead make it abstract by declaring it as a pure virtual function, using the =0 syntax. This prevents you from accidentally creating Object instances that can not be compared. This would have caught the missing pass-by-reference:
class Object {
public:
virtual bool equals(const Object& other) = 0;
}
Now, let's address your question:
This approach works in dynamically typed languages like JavaScript or Python, but it does not work in C++. During compilation, the compiler has to know where it finds the member value in the object other and its type. If you just pass in any Object, the compiler has no way of knowing it. And even you don't know: The type might be int or float. So neither you nor the compiler know whether there is a float value or an int value in the object you pass for comparison. If Integer objects should be comparable to both Integer and Float objects, you either need to comparison methods or you need a way to get a generic value of a common type. In this case, on machines with 32-Bit integers, every integer value is exactly representable in a double variable. You could add a second function to the Object class called as_double like this:
class Object {
public:
virtual bool equals(const Object &other) const = 0;
virtual double as_double() const = 0;
}
I also marked the methods const, which means you may call them on objects or using references you may not write to. Now you can implement Integer and Float like this:
class Integer: public Object {
public:
int value;
Integer(int val) {
value = val;
}
bool equals(const Object & other) const {
return value == other.as_double();
}
double as_double() const {
return value;
}
};
class Float: public Object {
public:
double value;
Float(double val) {
value = val;
}
bool equals(const Object & other) const {
return value == other.as_double();
}
double as_double() const {
return value;
}
};
And, if you look at the equals method, its now nearly the same for both types: You extract the value of argument as double, and compare it to the local value (in the case of Integer, the local value gets implicitly converted to double, too. So you could also use a generic comparison implementation that calls to_double on both objects, and you don't have to bother implementing equals in each subclass:
class Object {
public:
bool equals(const Object& other) const {
return as_double() == other.as_double();
}
virtual double as_double() const = 0;
}
Note, this only works because double actually is able to represent all values - even those stored in Integers. If your actual use-case does not have such a common type you can convert to, you need a more complicated solution.
Note that:
C++ is not a dynamically typed language, but a statically typed language. A variable's type is determined at compile time, and not runtime.
To have dynamic nature with the type, you need to have a polymorphic class - i.e. class having virtual functions. Through one or more virtual functions, you achieve the dynamic nature of the (class) type. The derived type (class) would (re)implement the necessary virtual functions
If you want to have types that seem dynamic but are static at compile time; then you can use templates. For example, an Add function can be:
template<typename Type>
Type Add(Type a, Type b)
{
return a+b;
}
And call them with any type:
Add(1,2); // int
Add(4.5, 5.0); // double
In the function, if you want to know the type, you can use certain helper functions like is_same:
Type Add(Type a, Type b)
{
if(std::is_same<Type, int>::value)
{
// Do whatever when 'int' is passed
}
...
}
The same approach can be used in class templates also. You may have (partial) template class specialization. vector with type bool is one example.
std::distance and std::advance are another examples which rely on the type of the container (in a simlpe sense) and are implemented differently (random access or sequence access).
The equal functions uses Object as the parameters type. Calling other.value does not work as you already pointed out Object does not have a parameter value. From the compiler point of view it will never get anything with a parameter value.
I suggest you rewrite the function definitions of equals to bool equals(Integer& other) and bool equals(Float& other).
Related
(Note: in case this feels like an X-Y problem, scroll below the separator for how I arrived at this question)
I am looking for a way to store pointers-to-member-functions (of different types) and compare them for equality. I need to store a mapping from pointer-to-member-function to an arbitrary object, and then search this mapping. It doesn't have to be an associative container, a linear search is fine. Also note that the pointers serve as mapping keys only, they are never dereferenced.
My current approach is this: when building the mapping, I reinterpret_cast the incoming pointer-to-member to one well-known type (void (MyClass::*)()) and insert it into the mapping. Something like this (error checking omitted for brevity):
template <class R, class... A)
void CallChecker::insert(R (MyClass::*key)(A...), Object value)
{
mapping.push_back(std::make_pair(reinterpret_cast<void (MyClass::*)()>(key), value));
}
Then on lookup, I perform the same cast and search by equality:
template <class R, class... A)
Object CallChecker::retrieve(R (MyClass::*key)(A...)) const
{
auto k = reinterpret_cast<void (MyClass::*)()>(key);
auto it = std::find_if(begin(mapping), end(mapping), [k](auto x) { return x.first == k; });
return it->second;
}
However, I am not sure that this will always work. While I believe it cannot produce false negatives (two equal pointers being reported as distinct), I am afraid it might produce false negatives (two pointers which were originally of different type could compare equal when cast to the "common" type). So my question is, is that the case? Or am I safe in using comparisons like this?
I know I am treading dangerously close to UB territory here. However, I don't mind a solution which works using behaviour which is not defined by the standard, but known to work in gcc and MSVC (my two target compilers).
So, the question is: is the comparison in a common type safe? Or would I be better off casting the stored pointer to the incoming type for the comparison (like this):
template <class R, class... A)
Object CallChecker::retrieve(R (MyClass::*key)(A...)) const
{
auto it = std::find_if(begin(mapping), end(mapping), [key](auto x) { return reinterpret_cast<R (MyClass::*)(A...)>(x.first) == key; });
return it->second;
}
Or will neither of these work in practice and I'm out of luck?
I am interested in the above properties of pointers-to-member, both in light of my actual task and to deepen my understanding of the language. Still, out of a sense of completeness (and in case somebody knows a better way), here is how I arrived at the original question.
I'm building a utility framework for helping unit-testing Qt4 signals (testing that the proper signals are emitted). My idea was to create a class CallChecker that would store validators (wrapped std::function objects) for slots, and be able to run them. The test would then create a class derived from this; that class would define slots which would run the corresponding validators. Here's an idea of usage (simplified):
class MyTester : public QObject, public CallChecker
{
Q_OBJECT
public slots:
void slot1(int i, char c) { CallChecker::checkCall(&MyTester::slot1, i, c); }
void slot2(bool b) { CallChecker::checkCall(&MyTester::slot2, b); }
};
void testRunner()
{
MyTester t;
connectToTestedSignals(t);
t.addCheck(&MyTester::slot1, [](int i, char c) { return i == 7; });
}
I have a working implementation (gcc on ideone) where CallChecker uses a std::vector of pairs, with the pointers-to-member cast to a common function type. After some fiddling with compiler flags (/vmg), I got this working in MSVC as well.
If you can suggest a better solution than lookup by pointer to member, I'll be happy to hear it. My goal is ease of use in the class implementing the test slots: I really want these slots to be simple one-liners. Using a textual representation of the slot signature (what Qt uses internally) is not really an option, as it's too susceptible to typos.
As I said in the comments, there is a way to unit test that a qt signal is emitted. You need to use QSignalSpy and link to QTestLib.
As they say in their documentation :
QSignalSpy can connect to any signal of any object and records its emission. QSignalSpy itself is a list of QVariant lists. Each emission of the signal will append one item to the list, containing the arguments of the signal.
You can also read their examples, but here is one of my unit tests that use google test :
class TestSomeControls : public testing::Test
{
public:
TestSomeControls() :
obj(),
ctrl1Dis( &obj, SIGNAL(DisableControl1(bool)) ),
ctrl2Dis( &obj, SIGNAL(DisableControl2(bool)) )
{
}
model::SomeControls obj;
QSignalSpy ctrl1Dis;
QSignalSpy ctrl2Dis;
};
TEST_F( TestSomeControls, OnControl1Clicked_untilControl1Disabled )
{
for ( int i = 0; i < 5; ++ i )
{
obj.OnControl1Clicked();
ASSERT_EQ( ctrl1Dis.count(), 0 );
}
obj.OnControl1Clicked();
ASSERT_EQ( ctrl1Dis.count(), 1 );
ASSERT_EQ( ctrl1Dis.takeFirst().at(0).toBool(), true );
}
Compare anything to anything.
#include <utility>
#include <memory>
#include <iostream>
struct Base
{
virtual bool operator== (const Base& other) const = 0;
virtual ~Base() {}
};
template <class T>
struct Holder : Base
{
Holder(T t) : t(t) {}
bool operator== (const Base& other) const
{
const Holder<T>* h = dynamic_cast<const Holder<T>*>(&other);
return (h && h->t == t);
}
private:
T t;
};
struct Any
{
template<class T>
Any(T t) : p(std::make_shared<Holder<T>>(t)) {}
bool operator== (const Any& other) const
{
return *p == *other.p;
}
private:
std::shared_ptr<Base> p;
};
int main ()
{
std::cout << (Any(2) == Any(2));
std::cout << (Any(2) == Any(3));
std::cout << (Any(2) == Any("foo"));
std::cout << (Any("foo") == Any("foo"));
std::cout << (Any("foo") == Any("bar"));
}
Implementation of operator< is deferred to the reader.
Important note Two pointers-to-member of different types will always compile unequal in this implementation, but it is possible that they will be equal in direct comparison after coercion to a common type. I.e &Foo::x and &Bar::x can be the same if Foo derives from Bar. Such behaviour cannot be easily added here.
If you first check that the typeid of both sides are the same, you can then use a type-erased function to cast both sides to the same type and compare in that type. (This is strictly necessary by the standard, as even if you can round-trip via a well-known type, there is no guarantee by the standard that comparisons in that type will have the same behaviour as comparisons in the original type.) Here's a sketch:
struct any_pmf_compare {
std::type_index ti;
void (any_pmf_compare::*pmf)();
bool (*comp)(const any_pmf_compare &, const any_pmf_compare &);
template<typename F>
any_pmf_compare(F f):
ti(typeid(F)),
pmf(reinterpret_cast<void (any_pmf_compare::*)()>(f)),
comp([](const any_pmf_compare &self, const any_pmf_compare &other) {
return reinterpret_cast<F>(self.pmf) == reinterpret_cast<F>(other.pmf);
})
{
}
};
bool operator==(const any_pmf_compare &lhs, const any_pmf_compare &rhs) {
return lhs.ti == rhs.ti && lhs.comp(lhs, rhs);
}
This is a narrow answer to the narrow question.
The standard states by implication and also in a footnote that a pointer to member cannot be converted to void*. The likely rationale is that a pointer to member could require more bytes of storage than a void*. Your compiler should forbid the reinterpret cast, and even it if does not you run a real risk of clashes. You can test on your target compilers, but the risk remains.
The standard will permit you to convert a 'pointer to member of X of type T1' to 'pointer to member of Y of type T2' when T1 and T2 are both function types. In other words, your strategy is permitted as long as the common type is a pointer to member function. I think this is what you intended. S5.2.10/10 in N3337. It does not however guarantee that two such pointers will compare equal, in the way that it does for pointers to objects. For example, if the implementation includes an encoded 'this' pointer, it just won't work.
The standard will permit you to store the pointer to member in a union. You can provide a char[] member that is likely to be long enough, and you can use an assert on sizeof to make sure that it is. Provided it's a 'standard layout' type, accessing the value through the char[] should have guaranteed behaviour. Personally, I would try this just to find out how big those pointers actually are! But the problem about possible non-canonical values remains.
My third suggestion is that you use the typeid of the pointer-to-member-function instead of the pointer itself. Typeid can be applied to any expression -- if it's good enough for reinterpret_cast it's good enough for typeid -- and the resultant value should be unique to the type, not the instance.
After that I'm out of ideas. You might have to redefine/renegotiate the problem in a quest for other solutions.
I'm trying to solve the following problem. I have a vector (it's a custom structure actually, but a vector is a good enough substitute for this issue) of pointers to a custom class A. Class A can actually store either a type_a pointer or a type_b pointer (these types are quite different and are not related to each other). Right now that's implemented by keeping both, setting them to NULL and then having a bunch of if/else statements later on to check which type it is and perform appropriate action.
class A {
public:
A() : p1(NULL), p2(NULL) {}
type_a * p1;
type_b * p2;
};
std::vector<A *> v;
...
if (v[0]->p1 != NULL) { /* do this */ }
else if (v[0]->p2 != NULL) { /* do that */ }
I plan to add more pointers to class A, and so the above is starting to become a hassle. The solution that I'm currently trying to make work is using boost::variant instead, to have:
class A {
public:
boost::variant<type_a*, type_b*> p;
};
The problem I have encountered though, is that one of my actions involves calling a function that would assign some values to a variable depending on what type of p I have. This is what it is now and the appropriate process_stuff function is called inside one of the above if/else statements:
class B { /*...*/ };
void process_stuff(type_a * p, B * b) {
b->m_var = p->fn1();
}
void process_stuff(type_b * p, B * b) {
b->m_var = p->fn2();
}
I can't get this to work with boost::static_visitor since (as far as I understand) I can't have a non-variant type as an argument in binary visitation, nor can I have a non-const operator() to make the second variable a member of the visitor class and modify that inside operator() with unary visitation. So I'm confused how to convert the above process_stuff function to play along with boost::variant.
Btw I'm not attached to boost::variant and would take other solutions.
You just need a stateful visitor. I'm typing this from a vague memory of exactly what visitors look like, but you should be able to fix any mistakes I make.
class process_stuff_visitor : public boost::static_visitor<void> {
B* m_b;
public:
process_stuff_visitor(B* b) : m_b(b) {}
void visit(type_a* a) const { m_b->m_var = a->fn1(); }
void visit(type_b* b) const { m_b->m_var = b->fn2(); }
};
// actual processing:
boost::apply_visitor(v[0], process_stuff_visitor(the_b));
Alternatively since you assign to the same member of B, you could just extract the value-generating part.
struct generate_stuff_visitor : public boost::static_visitor<TypeOfMVar> {
TypeOfMVar visit(type_a* a) const { return a->fn1(); }
TypeOfMVar visit(type_b* b) const { return b->fn2(); }
};
the_b->m_var = boost::apply_visitor(v[0], generate_stuff_visitor());
A very general object-oriented way of doing what you want to do (if I understand you correctly) is to create a virtual base class for types a and b (and any further types you want) which defines a pure virtual method. This method will return something different for each type (for example, type_b::method could return 'b', while type_a::method could return 'a'), so when you call the method on your unspecified type, you will be told what type it is.
From there, you can use the return value of the identifying method to be the subject of a switch statement, or some other conventional control structure to invoke the correct behavior.
I would like to define a completely generic mapping in c++ where I can map anything to anything.
I tried std::map but what should K and V be to make it general enough so I can map primitives or objects (as keys) to other primitives or objects (as values).
Or is there another mechanism I could use?
EDIT: For clarification, I am trying to define a relationship in the base class (from which all my classes are derived) that will allow me to attach arbitrary data to my classes. The simplest approach would be a be a name-value pair, where the above key is a string. I was wondering if i do something more generic?
Impossible- as it should be. Such a mapping would be worthless, since you can't depend on any meaningful behaviour of either key or value, and it's impossible to design either a binary relation or hash function that would be meaningful across "anything", or that could operate on any type, so it's nowhere near even the realm of possible.
Edit: There is nothing preventing std::unordered_map<std::string, boost::any>- or indeed, boost::any which happens to hold a std::unordered_map of some types.
However, your design appears highly questionable. You're basically completely subverting the compiler for no apparent benefit. Why would you derive every class from a common base? And why on earth would you want to attach arbitrary data? The usual way to put data in a class is to put it in the class, not blow all your safety and performance and sanity by trying to coerce C++ into being an interpreted language.
It is possible - so in this point I disagree with #DeadMG.
It is worthless - in this point full agreement,
However I do not understand that concept of answering, I mean "don't do it" answers instead "it can be done in this way, but my advise is don't do it". I do not pretend to be "life teacher" - I am just answering,
For values - use something like boost::any.
For keys - it is more complicated - because std::map defines order in keys. So generic keys must follow thess rules:
If real keys types are the same - use order from the real keys
If real keys are not the same - you must define order between types (like order of typeinfo::name())
Generic keys must be copy constructible
Let see my proposal for keys (using type erasure):
template <typename T>
struct GenKeyTypeOrder;
class GenKeyImplInt {
public:
// true if before other Key in other
virtual bool before(const GenKeyImplInt&) const = 0;
// type value
virtual int typeOrder() const = 0;
virtual GenKeyImplInt* clone() const = 0;
virtual ~GenKeyImplInt() {}
};
template <typename RealKey>
class GenKeyImpl : public GenKeyImplInt {
public:
GenKeyImpl(RealKey realKey) : realKey(realKey) {}
// true if before other Key in other
virtual bool before(const GenKeyImplInt& r) const
{
const GenKeyImpl* rp = dynamic_cast<const GenKeyImpl*>(&r);
if (rp) return realKey < rp->realKey;
return typeOrder() < r.typeOrder();
}
// type value
virtual int typeOrder() const { return GenKeyTypeOrder<RealKey>::VALUE; }
virtual GenKeyImpl* clone() const { return new GenKeyImpl(*this); }
private:
RealKey realKey;
};
class GenKey {
public:
// true if before other Key in other
friend bool operator < (const GenKey& l, const GenKey& r)
{
return l.impl->before(*r.impl);
}
template <typename T>
GenKey(T t) : impl(new GenKeyImpl<T>(t)) {}
GenKey(const GenKey& oth) : impl(oth.impl->clone()) {}
~GenKey() { delete impl; }
private:
GenKey& operator = (const GenKey& oth); // not defined
GenKeyImplInt* impl;
};
// define for every type you want be used as generic key
template <>
struct GenKeyTypeOrder<int> { enum { VALUE = 0 }; };
template <>
struct GenKeyTypeOrder<std::string> { enum { VALUE = 1 }; };
Full example at ideone
See also this article
You will need to make K and V be special objects.
The object will need to include what object type it is.
struct {
void *pointer;
string type;
// int type; // this is also possible
} Object;
The above Object can point to anything. However, it also needs something to say what type it is, hence the type string.
Then you need to be able to cast the pointer back to the required type, by reading what is in type.
Eg.
if (type == "int") cout << (int*)(myobject.pointer) << endl;
Anyways, if you do something like this, you are almost starting to build a loosely-typed interpreter, because for any operation you want to do with the object, you will need to check its type (whether you are adding, concatenating or printing the value to stdout).
It is probably better if you use a class object, and use inheritance to store any data you need.
class Object {
public virtual string to_string() {
return "";
}
};
Then if you want to store an integer:
class Integer : public Object {
int i;
public string to_string() {
char str[50];
sprintf(str,"%d",i);
return string(str);
}
public Integer operator=(int a) {
i=a;
return this;
}
};
This way you can define an interface of all the functions you want all objects to support.
Note that making the base Object class have virtual functions means that if you say:
Integer a;
a=5;
Object object = (Object)a;
cout << object.to_string << endl; // prints "5"
So that the function called is that defined by the actual (true) type of the object.
Is it possible to store a type name as a C++ variable? For example, like this:
type my_type = int; // or string, or Foo, or any other type
void* data = ...;
my_type* a = (my_type*) data;
I know that 99.9% of the time there's a better way to do what you want without resorting to casting void pointers, but I'm curious if C++ allows this sort of thing.
No, this is not possible in C++.
The RTTI typeid operator allows you to get some information about types at runtime: you can get the type's name and check whether it is equal to another type, but that's about it.
Not as written, but you could do something similar...
class Type
{
public:
virtual ~Type(){}
virtual void* allocate()const=0;
virtual void* cast(void* obj)const=0;
};
template<typename T> class TypeImpl : public Type
{
public:
virtual void* allocate()const{ return new T; }
virtual void* cast(void* obj)const{ return static_cast<T*>(obj); }
};
// ...
Type* type = new TypeImpl<int>;
void* myint = type->allocate();
// ...
This kind of thing can be extended depending on what features you need.
You can't do that in C++, but you can use the boost any library then test for the type it holds. Example:
bool is_int(const boost::any & operand)
{
return operand.type() == typeid(int);
}
http://www.boost.org/doc/libs/1_42_0/doc/html/any/s02.html
No you can't store the type directly as you want, but you can instead store the name of the type.
const char* str = typeid(int).name();
I guess whenever you planned to use that variable for comparison, you could instead at that time compare the str variable against the name() of the types.
const char* myType = typeid(int).name();
//....
//Some time later:
if(!strcmp(myType, typeid(int).name()))
{
//Do something
}
More info available here
Yes, if you code it yourself.
enum Foo_Type{
AFOO,
B_AFOO,
C_AFOO,
RUN
};
struct MyFoo{
Foo_Type m_type;
Boost::shared_ptr<Foo> m_foo;
}
as commented below, what I left out was that all these "foo" types would have to be related to Foo. Foo would, in essence, be your interface.
Today I had a similar problem while coding:
I had the need to store a polymoriphic data type (here named refobj) over wich call functions of the concrete classes implementing it. I need a solution that doesn't cast the variable explicitly because I need to reduce the amount of code.
My solution (but I haven't tested it yet) looks similar to a previous answer. Actually is quite an experimental solution. It look like this...
// interface to use in the function
class Type
{
public:
virtual void* getObj()const=0;
};
// here the static_cast with the "stored" type
template<typename T> class TypeImpl : public Type
{
public:
TypeImpl(T *obj) {myobj=obj;}
virtual void* getObj()const{ return static_cast<T*>(myobj); }
private:
T* myobj;
};
// here the type that will contain the polimorific type
// that I don't want to cast explicitly in my code
Type *refobj;
// here the "user code "
void userofTypes()
{
( refobj->getObj() ).c_str();
// getObj() should return a string type over which
// calling string concrete functions ...let's try!
}
void main()
{
refobj=new TypeImpl < string > ( new string("hello") );
userofTypes();
}
// it might seem absurd don't cast refobj explicitly, but of
// course there are situation in which it can be useful!
Types are not objects in C++ (where they are in Ruby, for instance), so you cannot store instances of a type. Actually, types never appear in the executing code (RTTI is just extra storage).
Based on your example, it looks like you're looking for typedefs.
typedef int Number;
Number one = 1;
Number* best = (Number*) one;
Note that a typedef isn't storing the type; it is aliasing the type.
A better process is to have a common base class containing a load method, and an interface for loaders. This would allow other parts of the program to load data generically without knowledge of the descendant class:
struct Load_Interface;
struct Loader
{
virtual void visit(Load_Interface&) = 0;
}
struct Load_Interface
{
virtual void accept_loader(Loader& l)
{
l.visit(*this);
}
};
This design avoids the need to know the types of objects.
I'm programming an interpreter for an experimental programming language (educational, fun,...)
So far, everything went well (Tokenizer & Parser), but I'm getting a huge problem with some of the data structures in the part that actually runs the tokenized and parsed code.
My programming language basically has only two types, int and string, and they are represented as C++ strings (std class) and ints
Here is a short version of the data structure that I use to pass values around:
enum DataType
{
Null,
Int,
String
}
class Symbol
{
public:
string identifier;
DataType type;
string stringValue;
int intValue;
}
I can't use union because string doesn't allow me to.
This structure above is beginning to give me a headache.
I have to scatter code like this everywhere in order to make it work, it is beginning to grow unmaintainable:
if( mySymbol.type == Int )
{
mySymbol.intValue = 1234;
} else {
mySymbol.stringValue = "abcde";
}
I use the Symbol data structure for variables, return values for functions and general representation of values in the programming language.
Is there any better way to solve this? I hope so!
What you are doing now is kind of a bastardization of a discriminated union. Problem is you're not using a union, and the functionality for the discriminated union is part of the Symbol class itself.
I suggest two alternatives, in order or preference:
1) Use a variant type. A variant type is like a discriminated union on steroids. One implementation can be found in Boost.
2) Create a proper discriminated union, defined separate from the Symbol class.
EDIT: A discriminated union doesn't actually have to be of type union. It could also be a struct.
The problem comes from the fact, that your symbol class is a type that contains two different types that you are trying to identify through the single type of the class Symbol.
It would be better to polymorphically create the symbols:
class Symbol
{
public:
virtual Symbol& operator = (int val) = 0; // Pure virtual
virtual Symbol& operator = (string val) = 0; // Pure virtual
private:
string identifier;
};
class IntSymbol : public Symbol
{
public:
virtual Symbol& operator = (int val)
{
this->val = val;
return *this; // to make multiple assignments possible
}
virtual Symbol& operator = (string val)
{
throw new exception("Programm error");
return *this; // to make it compile
}
private:
int val;
};
You do the same for the StringSymbol
I would probably use inheritance -- define a base class that implements the basic operations you want to support, so most of the other code can just use those. For example:
class value {
public:
virtual value &add(value const &other) = 0;
virtual value &assign(value const &other) = 0;
};
class string_val : public value {
std::string data;
public:
string_val &add(string_val const &other) { data += other.data; return *this; }
string_val &assign(string_val const &other) { data = other.data; return *this; }
};
Instead of using pure virtuals as I have here, you might prefer for the base class to actually define those functions, but have each throw an exception. These would be invoked only in cases where the derived classes did not provide an overload. This would be used for cases like attempting to divide "xyz" by "abc". With only two derived types this won't save a lot, but the more derived types you might add, the more it (potentially) saves.