2010-04-23 101 views
7

“简介”C++及其类型系统:如何处理多种类型的数据?

我对C++比较陌生。我经历了所有基本的东西,并设法为我的编程语言构建2-3个简单的解释器。

,给了仍然让我头疼的第一件事:实现我的C语言++

认为的类型系统:红宝石,Python和PHP和公司有很多内置显然在C中实现的类型。 所以我第一次尝试的是使我的语言有三种可能的类型:Int,String和Nil。

我想出了这一点:

enum ValueType 
{ 
    Int, String, Nil 
}; 

class Value 
{ 
public: 
    ValueType type; 
    int intVal; 
    string stringVal; 
}; 

呀,哇,我知道了。由于必须始终调用字符串分配器,因此传递这个类的速度非常缓慢。

下一次,我已经试过类似这样:

enum ValueType 
{ 
    Int, String, Nil 
}; 

extern string stringTable[255]; 
class Value 
{ 
public: 
    ValueType type; 
    int index; 
}; 

我会存储所有字符串stringTable,写自己的位置index。如果Value的类型是Int,我只是在index中存储了整数,那么使用int索引访问另一个int是没有意义的,或者?

反正上面也让我头痛。过了一段时间,从这里访问表格中的字符串,引用它并在那里复制它越来越多 - 我失去了控制。我不得不把翻译稿放下。

现在:好的,所以C和C++是静态类型的。

  • 如何上述语言的主要实现方式处理不同类型的在其方案(fixnums,大数,NUMS,字符串,数组,资源,...)?

  • 我该怎么做才能获得多种不同类型的最大速度?

  • 这些解决方案与我上面的简化版本相比如何?

+0

“内部,字符串,无”,约浮什么? – hhafez 2010-04-23 08:39:21

+1

我完全支持不带'float'的编程语言!否则,关于SO的第一个sub_Language问题将是“嘿,为什么在sub_Language中不会有0.1 + 0.2 == 0.3?它坏了!“。 – bobince 2010-04-23 09:08:37

+0

@sub:如果你是一个压倒一些答案的人,你应该重新考虑它。有没有理由低估了有效答案,其中一些答案可能是你完全不了解答案。如果不是你,那么做谁:请解释你认为错误的答案。这是实际改进系统的唯一方法。 – 2010-04-23 10:55:28

回答

4

在这里可以做几件不同的事情。不同的解决方案已经及时出现,其中大多数需要动态分配实际数据(boost :: variant可以避免为小对象使用动态分配的内存 - 感谢@MSalters)。

用纯C的方法:

商店类型信息和一个空指针到存储器,其具有根据所述类型信息(通常是一个枚举)被解释:

enum type_t { 
    integer, 
    string, 
    null 
}; 
typedef struct variable { 
    type_t type; 
    void * datum; 
} variable_t; 
void init_int_variable(variable_t * var, int value) 
{ 
    var->type = integer; 
   var->datum = malloc(sizeof(int)); 
    *((int)var->datum) = value; 
} 
void fini_variable(variable_t var) // optionally by pointer 
{ 
    free(var.datum); 
} 

在C++中可以改善这种方法通过使用类来简化使用,但更重要的是,您可以选择更复杂的解决方案,并使用现有库作为boost :: any或boost :: variant,为同一问题提供不同的解决方案。

boost :: any和boost :: variant都通过指向层次结构中虚拟类的指针和运算符将动态分配内存中的值存储到具体类型中。

+0

我不认为boost :: variant(或甚至boost :: any)_require_动态分配。如果可能的话,他们有特殊的技巧来回避。粗略地说,即使它们对堆中的大对象有'void *',那么该指针可以是其他成员用于存放小数据类型的联合的一部分。 – MSalters 2010-04-23 11:38:10

+0

@ MSalters:对。正如你所解释的,我没有看过变体的实现,它使用了一个缓冲区,根据所使用的特定类型重新解释。另一方面,任何提升都要简单得多,并无条件地使用动态分配的内存。 – 2010-04-23 12:21:22

1

至于速度,你说:

正是通过这 类各地为字符串分配器 不得不召集所有的时间极其缓慢。

你知道你应该通过引用传递对象绝大多数时间?您的解决方案适用于简单的解释器。

+0

大部分时间并不那么容易。由于许多函数暂时修改了值,我不得不制作多个副本。 – sub 2010-04-23 08:30:22

+1

@sub那听起来很可疑。我看不出为什么你会改变一个值,一旦创建,除非用户分配给它。 – 2010-04-23 08:32:24

4

一个明显的解决方案是定义一种类型的层次结构:

class Type 
{ 
}; 

class Int : public Type 
{ 
}; 

class String : public Type 
{ 
}; 

等。作为一个完整的例子,让我们写一个小语言的解释器。该语言允许声明变量是这样的:

var a 10 

这将创建一个Int对象,分配给它的价值10并将其存储在一个变量的表名a下。操作可以在变量上调用。例如在两个int值的加法运算的样子:

+ a b 

下面是解释器的完整代码:

#include <iostream> 
#include <string> 
#include <vector> 
#include <sstream> 
#include <cstdlib> 
#include <map> 

// The base Type object from which all data types are derived. 
class Type 
{ 
public: 
    typedef std::vector<Type*> TypeVector; 
    virtual ~Type() { } 

    // Some functions that you may want all types of objects to support: 

    // Returns the string representation of the object. 
    virtual const std::string toString() const = 0; 
    // Returns true if other_obj is the same as this. 
    virtual bool equals (const Type &other_obj) = 0; 
    // Invokes an operation on this object with the objects in args 
    // as arguments. 
    virtual Type* invoke (const std::string &opr, const TypeVector &args) = 0; 
}; 

// An implementation of Type to represent an integer. The C++ int is 
// used to actually store the value. As a consequence this type is 
// machine dependent, which might not be what you want for a real 
// high-level language. 
class Int : public Type 
{ 
public: 
    Int() : value_ (0), ret_ (NULL) { } 
    Int (int v) : value_ (v), ret_ (NULL) { } 
    Int (const std::string &v) : value_ (atoi (v.c_str())), ret_ (NULL) { } 
    virtual ~Int() 
    { 
    delete ret_; 
    } 
    virtual const std::string toString() const 
    { 
    std::ostringstream out; 
    out << value_; 
    return out.str(); 
    } 
    virtual bool equals (const Type &other_obj) 
    {  
    if (&other_obj == this) 
     return true; 
    try 
     { 
     const Int &i = dynamic_cast<const Int&> (other_obj); 
     return value_ == i.value_; 
     } 
    catch (std::bad_cast ex) 
     { 
     return false; 
     } 
    } 
    // As of now, Int supports only addition, represented by '+'. 
    virtual Type* invoke (const std::string &opr, const TypeVector &args)  
    { 
    if (opr == "+") 
     { 
     return add (args); 
     } 
    return NULL; 
    } 
private: 
    Type* add (const TypeVector &args) 
    { 
    if (ret_ == NULL) ret_ = new Int; 
    Int *i = dynamic_cast<Int*> (ret_); 
    Int *arg = dynamic_cast<Int*> (args[0]); 
    i->value_ = value_ + arg->value_; 
    return ret_; 
    } 
    int value_; 
    Type *ret_; 
}; 

// We use std::map as a symbol (or variable) table. 
typedef std::map<std::string, Type*> VarsTable; 
typedef std::vector<std::string> Tokens; 

// A simple tokenizer for our language. Takes a line and 
// tokenizes it based on whitespaces. 
static void 
tokenize (const std::string &line, Tokens &tokens) 
{ 
    std::istringstream in (line, std::istringstream::in); 
    while (!in.eof()) 
    { 
     std::string token; 
     in >> token; 
     tokens.push_back (token); 
    } 
} 

// Maps varName to an Int object in the symbol table. To support 
// other Types, we need a more complex interpreter that actually infers 
// the type of object by looking at the format of value. 
static void 
setVar (const std::string &varName, const std::string &value, 
     VarsTable &vars) 
{ 
    Type *t = new Int (value); 
    vars[varName] = t; 
} 

// Returns a previously mapped value from the symbol table. 
static Type * 
getVar (const std::string &varName, const VarsTable &vars) 
{ 
    VarsTable::const_iterator iter = vars.find (varName); 
    if (iter == vars.end()) 
    { 
     std::cout << "Variable " << varName 
       << " not found." << std::endl; 
     return NULL; 
    } 
    return const_cast<Type*> (iter->second); 
} 

// Invokes opr on the object mapped to the name var01. 
// opr should represent a binary operation. var02 will 
// be pushed to the args vector. The string represenation of 
// the result is printed to the console. 
static void 
invoke (const std::string &opr, const std::string &var01, 
     const std::string &var02, const VarsTable &vars) 
{ 
    Type::TypeVector args; 
    Type *arg01 = getVar (var01, vars); 
    if (arg01 == NULL) return; 
    Type *arg02 = getVar (var02, vars); 
    if (arg02 == NULL) return; 
    args.push_back (arg02); 
    Type *ret = NULL; 
    if ((ret = arg01->invoke (opr, args)) != NULL) 
    std::cout << "=> " << ret->toString() << std::endl; 
    else 
    std::cout << "Failed to invoke " << opr << " on " 
       << var01 << std::endl; 
} 

// A simple REPL for our language. Type 'quit' to exit 
// the loop. 
int 
main (int argc, char **argv) 
{ 
    VarsTable vars; 
    std::string line; 
    while (std::getline (std::cin, line)) 
    { 
     if (line == "quit") 
     break; 
     else 
     { 
      Tokens tokens; 
      tokenize (line, tokens); 
      if (tokens.size() != 3) 
      { 
       std::cout << "Invalid expression." << std::endl; 
       continue; 
      } 
      if (tokens[0] == "var") 
      setVar (tokens[1], tokens[2], vars); 
      else 
      invoke (tokens[0], tokens[1], tokens[2], vars); 
     } 
    } 
    return 0; 
} 

与解释的样品相互作用:

/home/me $ ./mylang 

var a 10 
var b 20 
+ a b 
30 
+ a c 
Variable c not found. 
quit 
+0

我可以将它们全部存储在单个数组中吗?或者它们是完全不同的类型,如string vs int?我对此比较陌生。 – sub 2010-04-23 08:31:15

+0

+1 @Vijay Mathew:我的想法正好 – hhafez 2010-04-23 08:44:00

+0

@Vijay:你能解释一下如何以及在哪里存储这些类的实例吗? – sub 2010-04-23 08:48:51

1

C++是一种强类型语言。我可以看到你是来自非语言类型的语言,仍然用这些术语思考。

如果你真的需要在变量中存储几种类型,然后看看boost::any

但是,如果你正在实现一个解释器,你应该使用继承和代表特定类型的类。

+0

这不是我的问题。你没有告诉我*如何*和*哪里*来存储这些类的实例。 – sub 2010-04-23 08:45:55

+0

我有upvoted,因为答案很好:使用boost :: any。我不明白这不是你的问题。 – 2010-04-23 10:56:02

+0

+1,同意。 boost :: any是写入问题的解决方案,即使它不是(sub)头部问题的(完整)答案。 – MSalters 2010-04-23 11:35:32

0

据维杰的解决方案的实施将是:

Type* array; 
// to initialize the array 
array = new Type(size_of_array); 
// when you want to add values 
array[0] = new Int(42); 
// to add another string value 
array[1] = new String("fourty two"); 

位从他的代码缺少的是如何提取这些价值观......这是我的版本(其实我了解到这个来自食人魔,并修改为我的喜欢)。

用法是一样的东西:

Any array[4]; 
// Automatically understands it's an integer 
array[0] = Any(1); 
// But let's say you want the number to be thought of as float 
array[1] = Any<float>(2); 
// What about string? 
array[2] = Any<std::string>("fourty two"); 
// Note that this gets the compiler thinking it's a char* 
// instead of std::string 
array[3] = Any("Sometimes it just turns out to be what you don't want!"); 

好了,现在看,如果某一元素是字符串:

if(array[2].isType<std::string>() 
{ 
    // Extract the string value. 
    std::string val = array[2].cast<std::string>(); 
    // Make the string do your bidding!!!... /evilgrin 
    // WAIT! But what if you want to directly manipulate 
    // the value in the array? 
    std::string& val1 = array[2].cast<std::string>(); 
    // HOHOHO... now any changes to val1 affects the value 
    // in the array ;) 
} 

的任何类的代码如下所示。随意使用它,但你喜欢:)。希望这可以帮助!

在头文件...说Any.h

#include <typeinfo> 
    #include <exception> 

    /* 
    * \class Any 
    * \brief A variant type to hold any type of value. 
    * \detail This class can be used to store values whose types are not 
    *  known before hand, like to store user-data. 
    */ 
    class Any 
    { 
    public: 
     /*! 
     * \brief Default constructor. 
     */ 

    Any(void); 

    /*! 
    * \brief Constructor that accepts a default user-defined value. 
    * \detail This constructor copies that user-defined value into a 
    *  place holder. This constructor is explicit to avoid the compiler 
    *  to call this constructor implicitly when the user didn't want 
    *  the conversion to happen. 
    * \param val const reference to the value to be stored. 
    */ 
    template <typename ValueType> 
    explicit Any(const ValueType& val); 

    /*! 
    * \brief Copy constructor. 
    * \param other The \c Any variable to be copied into this. 
    */ 
    Any(const Any& other); 

    /*! 
    * \brief Destructor, does nothing other than destroying the place holder. 
    */ 
    ~Any(void); 

    /*! 
    * \brief Gets the type of the value stored by this class. 
    * \detail This function uses typeid operator to determine the type 
    *  of the value it stores. 
    * \remarks If the place holder is empty it will return Touchscape::VOID_TYPE. 
    *  It is wise to check if this is empty by using the function Any::isEmpty(). 
    */ 
    const std::type_info& getType() const; 

    /*! 
    * \brief Function to verify type of the stored value. 
    * \detail This function can be used to verify the type of the stored value. 
    * Usage: 
    * \code 
    * int i; 
    * Touchscape::Any int_any(i); 
    * // Later in your code... 
    * if (int_any.isType<int>()) 
    * { 
    *  // Do something with int_any. 
    * } 
    * \endcode 
    * \return \c true if the type matches, false otherwise. 
    */ 
    template <typename T> 
    bool isType() const; 

    /*! 
    * \brief Checks if the type stored can be converted 'dynamically' 
    *  to the requested type. 
    * \detail This would useful when the type stored is a base class 
    *  and you would like to verify if it can be converted to type 
    *  the user wants. 
    * Example: 
    * \code 
    * class Base 
    * { 
    *  // class implementation. 
    * }; 
    * class Derived : public Base 
    * { 
    *  // class implementation. 
    * }; 
    * 
    * // In your implementation function. 
    * { 
    *  //... 
    *  // Somewhere in your code. 
    *  Base* a = new Derived(); 
    *  Touchscape::Any user_data(a); 
    *  my_object.setUserData(user_data); 
    *  // Then when you need to know the user-data type 
    *  if(my_object.getUserData().isDynamicType<Derived>()) 
    *  { 
    *   // Do something with the user data 
    *  } 
    * } 
    * \endcode 
    * \return \c true if the value stored can be dynamically casted to the target type. 
    * \deprecated This function will be removed and/or changed in the future. 
    */ 
    template <typename T> 
    bool isDynamicType() const; 

    /*! 
    * \brief Convert the value stored to the required type. 
    * \detail This function is used just like a static-cast to retrieve 
    *  the stored value. 
    * \return A reference to the stored value. 
    * \warning This function will throw std::bad_cast exception if it 
    *  finds the target type to be incorrect. 
    */ 
    template <typename T> 
    T& cast(); 

    /*! 
    * \brief Convert the value stored to the required type (const version). 
    * \detail This function is used just like static_cast to retrieve 
    *  the stored value. 
    * \return A \c const reference to the stored value. 
    * \warning This function will throw std::bad_cast exception if it 
    *  finds the target type to be incorrect. 
    */ 
    template <typename T> 
    const T& cast() const; 

    /*! 
    * \brief Dynamically converts the stored value to the target type 
    * \detail This function is just like dynamic_cast to retrieve 
    *  the stored value to the target type. 
    * \return A reference to the stored value. 
    * \warning This function will throw std::bad_cast exception if it 
    *  finds that the value cannot be dynamically converted to the target type. 
    * \deprecated This function will be removed and/or changed in the future. 
    */ 
    template <typename T> 
    T& dynamicCast(); 

    /*! 
    * \brief Dynamically converts the stored value to the target type (const version) 
    * \detail This function is just like dynamic_cast to retrieve 
    *  the stored value to the target type. 
    * \return A const reference to the stored value. 
    * \warning This function will throw std::bad_cast exception if it 
    *  finds that the value cannot be dynamically converted to the target type. 
    * \deprecated This function will be removed and/or changed in the future. 
    */ 
    template <typename T> 
    const T& dynamicCast() const; 

    /*! 
    * \brief Swaps the contents with another \c Any variable. 
    * \return reference to this instance. 
    */ 
    Any& swap(Any& other); 

    /*! 
    * \brief Checks if the place holder is empty. 
    * \return \c true if the the place holder is empty, \c false otherwise. 
    */ 
    bool isEmpty() const; 

    /*! 
    * \brief Checks if the place holder is \b not empty. 
    * \return \c true if the the place holder is not empty, \c false otherwise. 
    * \remarks This is just a lazy programmer's attempt to make the code look elegant. 
    */ 
    bool isNotEmpty() const; 

    /*! 
    * \brief Assignment operator 
    * \detail Assigns a 'raw' value to this instance. 
    * \return Reference to this instance after assignment. 
    */ 
    template <typename ValueType> 
    Any& operator = (const ValueType& rhs); 

    /*! 
    * \brief Default assignment operator 
    * \detail Assigns another \c Any type to this one. 
    * \return Reference to this instance after assignment. 
    */ 
    Any& operator = (const Any& rhs); 

    /*! 
    * \brief Boolean equality operator 
    */ 
    bool operator == (const Any& other) const; 

    /*! 
    * \brief Boolean equality operator that accepts a 'raw' type. 
    */ 
    template<typename ValueType> 
    bool operator == (const ValueType& other) const; 

    /*! 
    * \brief Boolean inequality operator 
    */ 
    bool operator != (const Any& other) const; 

    /*! 
    * \brief Boolean inequality operator that accepts a 'raw' type. 
    */ 


     template<typename ValueType> 
     bool operator != (const ValueType& other) const; 
    protected: 
     /*! 
     * \class PlaceHolder 
     * \brief The place holder base class 
     * \detail The base class for the actual 'type'd class that stores 
     *  the value for T 

ouchscape::Any. 
    */ 
    class PlaceHolder 
    { 
    public: 

     /*! 
     * \brief Virtual destructor. 
     */ 
     virtual ~PlaceHolder(){} 

     /*! 
     * \brief Gets the \c type_info of the value stored. 
     * \return (const std::type_info&) The typeid of the value stored. 
     */ 
     virtual const std::type_info& getType() const = 0; 
     /*! 
     * \brief Clones this instance. 
     * \return (PlaceHolder*) Cloned instance. 
     */ 
     virtual PlaceHolder* clone() const = 0; 
    }; 

    /*! 
    * \class PlaceHolderImpl 
    * \brief The class that ultimately keeps hold of the value stored 
    *  in Touchscape::Any. 
    */ 
    template <typename ValueType> 
    class PlaceHolderImpl : public PlaceHolder 
    { 
    public: 
     /*! 
     * \brief The only constructor allowed. 
     * \param val The value to store. 
     */ 
     PlaceHolderImpl(const ValueType& val) 
      :m_value(val){} 
     /*! 
     * \brief The destructor. 
     * \detail Does nothing 
     */ 
     ~PlaceHolderImpl(){} 

     /*! 
     * \copydoc Touchscape::PlaceHolder::getType() 
     */ 
     const std::type_info& getType() const 
     { 
      return typeid(ValueType); 
     } 

     /*! 
     * \copydoc Touchscape::PlaceHolder::clone() 
     */ 
     PlaceHolder* clone() const 
     { 
      return new PlaceHolderImpl<ValueType>(m_value); 
     } 

     ValueType m_value; 
    }; 

    PlaceHolder* m_content; 
}; 

/************************************************************************/ 
/* Template code implementation section         */ 
/************************************************************************/ 
template <typename ValueType> 
Any::Any(const ValueType& val) 
    :m_content(new PlaceHolderImpl<ValueType>(val)) 
{ 
} 
//--------------------------------------------------------------------- 
template <typename T> 
bool Any::isType() const 
{ 
    bool result = m_content?m_content->getType() == typeid(T):false; 
    return result; 
} 
//--------------------------------------------------------------------- 
template <typename T> 
bool Any::isDynamicType() const 
{ 
    bool result = m_content 
     ?dynamic_cast<T>(static_cast<PlaceHolderImpl<T>*>(m_content)->m_value)!=NULL 
     :false; 
    return result; 
} 
//--------------------------------------------------------------------- 
template <typename T> 
T& Any::cast() 
{ 
    if (getType() != VOID_TYPE && isType<T>()) 
    { 
     T& result = static_cast<PlaceHolderImpl<T>*>(m_content)->m_value; 
     return result; 
    } 
    StringStream ss; 
    ss<<"Cannot convert '"<<getType().name()<<"' to '"<<typeid(T).name()<<"'. Did you mean to use dynamicCast() to cast to a different type?"; 
    throw std::bad_cast(ss.str().c_str()); 
} 
//--------------------------------------------------------------------- 
template <typename T> 
const T& Any::cast() const 
{ 
    Any& _this = const_cast<Any&>(*this); 
    return _this.cast<T>(); 
} 
//--------------------------------------------------------------------- 
template <typename T> 
T& Any::dynamicCast() 
{ 
    T* result = dynamic_cast<T>(static_cast<PlaceHolderImpl<T>*>(m_content)->m_value); 
    if (result == NULL) 
    { 
     StringStream ss; 
     ss<<"Cannot convert '"<<getType().name()<<"' to '"<<typeid(T)<<"'."; 
     throw std::bad_cast(ss.str().c_str()); 
    } 
    return *result; 
} 
//--------------------------------------------------------------------- 
template <typename T> 
const T& Any::dynamicCast() const 
{ 
    Any& _this = const_cast<Any&>(*this); 
    return _this.dynamicCast<T>(); 
} 
//--------------------------------------------------------------------- 
template <typename ValueType> 
Any& Any::operator = (const ValueType& rhs) 
{ 
    Any(rhs).swap(*this); 
    return *this; 
} 
//--------------------------------------------------------------------- 
template <typename ValueType> 
bool Any::operator == (const ValueType& rhs) const 
{ 
    bool result = m_content == rhs; 
    return result; 
} 
//--------------------------------------------------------------------- 
template <typename ValueType> 
bool Any::operator != (const ValueType& rhs) const 
{ 
    bool result = m_content != rhs; 
    return result; 
} 

现在在CPP文件... Any.cpp

#include "Any.h" 

static const std::type_info& VOID_TYPE(typeid(void)); 

Any::Any(void) 
    :m_content(NULL) 
{ 
} 
//--------------------------------------------------------------------- 
Any::Any(const Any& other) 
    :m_content(other.m_content?other.m_content->clone():NULL) 
{ 
} 
//--------------------------------------------------------------------- 
Any::~Any(void) 
{ 
    SafeDelete(m_content); 
} 
//--------------------------------------------------------------------- 
const std::type_info& Any::getType() const 
{ 
    return m_content?m_content->getType():VOID_TYPE; 
} 
//--------------------------------------------------------------------- 
Any& Any::swap(Any& other) 
{ 
    std::swap(m_content, other.m_content); 
    return *this; 
} 
//--------------------------------------------------------------------- 
Any& Any::operator=(const Any& rhs) 
{ 
    Any(rhs).swap(*this); 
    return *this; 
} 
//--------------------------------------------------------------------- 
bool Any::isEmpty() const 
{ 
    bool is_empty = m_content == NULL; 
    return is_empty; 
} 
//--------------------------------------------------------------------- 
bool Any::isNotEmpty() const 
{ 
    bool is_not_empty = m_content != NULL; 
    return is_not_empty; 
} 
//--------------------------------------------------------------------- 
bool Any::operator==(const Any& other) const 
{ 
    bool result = m_content == other.m_content; 
    return result; 
} 
//--------------------------------------------------------------------- 
bool Any::operator!=(const Any& other) const 
{ 
    bool result = m_content != other.m_content; 
    return result; 
} 
+0

忘了提及。速度方面,它实际上是一个指向PlaceHolder clas实例的指针,该实例存储指向正在传递的值的指针。我估计没有太多的开销。它是类型安全的,不需要像Boost这样的复杂库。 – 2010-04-23 10:23:45

相关问题