传递任何数据类型以在C ++中运行

时间:2014-10-27 03:26:44

标签: c++ pointers struct

出于我的一个项目的目的,我想制作一个可以包含任何类型数据的typedef struct。我创建了一个存储在enum中的数据,以及实际保存数据的void *

在我目前的尝试中,它在整数方面效果很好,但是当我尝试添加其他类型时,例如std::stringfloat,它的效果并不好。

我尝试过几种不同的尝试将数据放入容器并将其取出的方法,但到目前为止还没有一种方法适用于所有内容。

最好的方法是什么?我想将任何类型的数据填充到结构中,并能够以相同的形式检索数据。

编辑:

typedef struct{
    DataTypeInt,
    DataTypeFloat,
    DataTypeString
}DataType;

typedef struct{
    DataType type;
    void *data;
}Data;

起初我只使用整数进行测试,我只需使用(void *)foo来输入数据。然后放(int)bar.data以获取数据。这对于int类型非常有效,但如果我尝试std::stringfloat

,我会收到编译器错误

在使用不同的指针和转换配置并搜索互联网后,我最终只是简单地foo.data = &bar输入数据,float foo = *(float*)bar.data来获取数据。这并没有给出任何错误,但数据并没有正确显示。来自另一方的任何intfloat都是0,字符串将是随机符号。

我还尝试使用包装器结构的这两个方法来包含每种数据类型,然后将其作为数据传递。

4 个答案:

答案 0 :(得分:2)

1)如果您需要变体,并且不介意使用Boost,请考虑Boost.Variant或Boost.Any。它干净,健壮,成熟。

2)使用重载来实现非联合变体。 (真正的工会的问题是针对非POD类型,它的丑陋甚至经验丰富的开发人员忘记了如何做到这一点)。最好远离。

class Variant 
{
    int i;
    float j;
    std::string s;

    // Add all of your typed getter and setters
}

使用重载创建一个非联合包装类,每个类型都有一个成员,然后添加getter / setter方法,并为每种类型添加覆盖。只要您不打算创建数百万这种类型,它就比使用void *

更安全

3)使用子类型多态 - 一个基类,它指定所有不同的可能类型,然后通过特定实现进行扩展。

class VariantBase
{
public:
    virtual int          get_int()    { throw new "Not implemented"; }
    virtual float        get_float()  { throw new "Not implemented"; }
    virtual std::string  get_string() { throw new "Not implemented"; }
}

class VariantInt
{
    int val;
public:
    virtual int          get_int()      { return val; }
    virtual void         set_int(int i) { val = i; }
}

class VariantString
{
   ...
}

4)如果您习惯于C#,其中所有类型都派生自System.Object,那么与C ++最接近的就是void *,但是,与C#不同,你隐藏在void *中的东西会丢失它的类型暂时,你不能使用反射。更重要的是,如果你踩踏它,垃圾收集器不会拯救你。因此,您应该在void *周围实施一个安全的Variant包装,而不是仅仅希望最好,以保护您自己。

您可以将对象放入void *

void * vp = obj;

但要解决这个问题,你需要使用C ++演员。

MyClass * obj = static_cast<MyClass*>(vp);

您可以在C ++规则中使用void *实现安全的Variant。记住,有Boost.Variant(1)。我把它放在这里只是为了证明。这是危险的,通常意味着你通过了其他有效的解决方案。但是,C程序员经常使用该技术。 C ++语言创建者Bjarne Stroustrup明确地认为这是合法且有价值的,我认为它只是C ++的一部分。这里的一般想法是一个简单的3型变体。更好的C ++开发人员可以使用模板完成大部分工作,但由于您不熟悉C ++,因此我希望您能够理解非模板解决方案。使用void *时,在尝试转换之前必须始终确定指针所指的类型,因此一个好的规则是将所有访问器包装在验证枚举值的函数中。首先检查你的枚举值,如果不匹配(有人在字符串上调用get_int),则抛出异常,尽管某些变体支持转换。由于您正在处理指针,因此set_val_type()方法将首先删除旧内存。注意使用static_cast<>,如果你想要干净,定义的代码,这是你应该在C ++中转换void *的唯一方法。

enum VariantValType { None, Int, Float, StdString };

class Variant
{
     VariantValType valType;
     void *val;
public:
     Variant() : val(nullptr), valType(None) {}
     ~Variant() { reset_val(); }

     void set_val_type(VariantValType t) {
          if (t == valType)
               return;
          reset_val();
          valType = t;
     }

     void reset_val() {
          switch (valType) {
          case None:          break;
          case Int:           delete (static_cast<int*>(val)); break;
          case Float:         delete (static_cast<float*>(val)); break;
          case StdString:     delete (static_cast<std::string*>(val)); break;
          default:            throw "Unknown value type"; break;
          }
          valType = None;
          val = nullptr;
     }

     int get_int() {
          if (valType != Int)
               throw "Variant type mismatch";
          return *(static_cast<int*>(val));
     }

     void set_int(int i) {
          set_val_type(Int);
          val = new int{ i };
     }

     float get_float() {
          if (valType != Float)
               throw "Variant type mismatch";
          return *(static_cast<float*>(val));
     }

     void set_float(float f) {
          set_val_type(Float);
          val = new float{ f };
     }

     std::string& get_string() {
          // If you like, implement conversion here with std::to_string() like so
          // if(valType == Int) return std::to_string(*(static_cast<int*>(val)));

          if (valType != StdString)
               throw "Variant type mismatch";
          return *(static_cast<std::string*>(val));
     }

     void set_string(const std::string& s) {
          set_val_type(StdString);
          val = new std::string(s);
     }
};

答案 1 :(得分:1)

您传递一个指向数据的任意指针并对其进行操作。请参阅void*类型。

答案 2 :(得分:0)

以下是定义通用数据类型并在代码中使用它的完整演示。我不久前写过这个演示。现在它已更新,以支持您持有std :: string数据类型的要求。对浮动的支持以“双重”形式出现。数据类型。

您可以向其添加任何数据类型。分配数据类型时应小心。这意味着,你知道自己在做什么。

将代码下方的粘贴复制到控制台应用程序,看看它是如何工作的。

#include "windows.h"
#include "vector"
#include "string"

using namespace std;

enum GENERIC_DATA_TYPE
{
   GENERIC_TYPE_NONE = 0,
   GENERIC_TYPE_INT,
   GENERIC_TYPE_SHORT,
   GENERIC_TYPE_DOUBLE,
   GENERIC_TYPE_CHAR,
   GENERIC_TYPE_STD_STRING,
   GENERIC_TYPE_STD_WSTRING,
};

#define SAFE_ARRAY_DELETE( ptrArray ) \
delete[] ptrArray;\
ptrArray = 0;

#define SAFE_DELETE( ptrPointer ) \
delete ptrPointer;\
ptrPointer = 0;

struct GENERIC_DATA
{
   GENERIC_DATA_TYPE m_DataType;
   UINT              m_DataSize;
   void*             m_ptrData;

   GENERIC_DATA() : m_DataType( GENERIC_TYPE_NONE ),
                    m_DataSize( 0 ),
                    m_ptrData( 0 )
   {
   }

   ~GENERIC_DATA()
   {
       CleanMemory();
   }

   bool CleanMemory()
   {
       try
       {
           switch( m_DataType )
           {
               case GENERIC_TYPE_INT:
               {
                   int* ptrInt = reinterpret_cast<int*>( m_ptrData );
                   SAFE_ARRAY_DELETE( ptrInt );
                   break;
               }
               case GENERIC_TYPE_SHORT:
               {
                   short* ptrShort = reinterpret_cast<short*>( m_ptrData );
                   SAFE_ARRAY_DELETE( ptrShort );
                   break;
               }
               case GENERIC_TYPE_DOUBLE:
               {
                   double* ptrDouble = reinterpret_cast<double*>( m_ptrData );
                   SAFE_ARRAY_DELETE( ptrDouble );
                   break;
               }
               case GENERIC_TYPE_CHAR:
               {
                   // Since string is kept as an array of string,
                   // we need to iterate each string
                   // and delete.
                   char** ptrString = reinterpret_cast<char**>( m_ptrData );
                   for( UINT uCounter = 0; m_DataSize > uCounter; ++uCounter )
                   {
                        SAFE_ARRAY_DELETE( ptrString[uCounter]);
                   }

                   // Now delete the double pointer.
                   SAFE_ARRAY_DELETE( ptrString );
                   break;
               }
               case GENERIC_TYPE_STD_STRING:
                {
                    string* pstdString = reinterpret_cast<string*>( m_ptrData );
                    SAFE_DELETE ( pstdString );
                    break;
                }
               case GENERIC_TYPE_STD_WSTRING:
                {
                    wstring* pstdString = reinterpret_cast<wstring*>( m_ptrData );
                    SAFE_DELETE ( pstdString );
                    break;
                }
           }
           m_DataSize = 0;
           m_DataType = GENERIC_TYPE_NONE;
           return true;
       }
       catch( ... )
       {
       }
       return false;
   }
}; 

typedef vector<GENERIC_DATA*> GENERIC_DATA_VECTOR;

int main()
{
    GENERIC_DATA_VECTOR vData;

    // Allocate memory to hold the data
    GENERIC_DATA* ptrData = new GENERIC_DATA();

    // PUT SOME INTERGERS
    // Of course the array size would be determined at runtime.
    const int INT_COUNT = 10;
    int* ptrIntArray = new int[INT_COUNT]; 

    UINT nCounter = 0;

    // Fill ptrIntArray with some integers
    for( nCounter = 0; INT_COUNT > nCounter; ++nCounter )
    {
        ptrIntArray[nCounter] = rand();
    }
    // It is verly important to set the correct type here.
    ptrData->m_DataType = GENERIC_TYPE_INT;
    ptrData->m_DataSize = INT_COUNT;
    ptrData->m_ptrData  = ptrIntArray;

    // Now put the data in the vector;
    vData.push_back( ptrData );


    // PUT A STRING
    string* pstrString = new string();
    *pstrString = "Helo World";

    ptrData = new GENERIC_DATA();
    // It is verly important to set the correct type here.
    ptrData->m_DataType = GENERIC_TYPE_STD_STRING;
    ptrData->m_DataSize = 1;
    ptrData->m_ptrData  = pstrString;

    // Now put the data in the vector;
    vData.push_back( ptrData );



    // Now, at a later time we can manipulate each
    // or the required type in the vector as below.
    GENERIC_DATA_VECTOR::iterator DATA_VECTOR_END = vData.end();
    GENERIC_DATA_VECTOR::iterator itrData = vData.begin();
    GENERIC_DATA* ptrDataToProcess = 0;
    for( ; itrData != DATA_VECTOR_END; ++itrData )
    {
        ptrDataToProcess = ( *itrData );

        // Look for integer
        if( GENERIC_TYPE_INT == ptrDataToProcess->m_DataType )
        {
            int* ptrIntArray = reinterpret_cast<int*>( ptrDataToProcess->m_ptrData );

            // Process integers
            for( nCounter = 0; ptrDataToProcess->m_DataSize > nCounter; ++nCounter )
            {
                printf( "\n %d", ptrIntArray[nCounter]);
            }
            continue; // Continue to next data.
        }

        // Look for string
        if( GENERIC_TYPE_STD_STRING == ptrDataToProcess->m_DataType )
        {
            string* pstrString = reinterpret_cast<string*>( ptrDataToProcess->m_ptrData );

            // Process the string
            printf( "\n %s", pstrString->c_str());
        }
        continue; // Continue to next data.
    }

    // Once we finish with the data, iterate the vector and delete each entry.
    DATA_VECTOR_END = vData.end();
    itrData = vData.begin();
    for( ; itrData != DATA_VECTOR_END; ++itrData )
    {
        delete ( *itrData );
    }

    return 0;
}

答案 3 :(得分:-1)

你可以重载那个函数..这里是简单的例子..

int AddI(int nX, int nY)
{
return nX + nY;
}

double AddD(double dX, double dY)
{
return dX + dY;
}