我可以“浏览”结构的成员来简化构建BDD类吗?

时间:2017-10-13 12:16:22

标签: c++ struct bdd

我正在构建一个自定义BDD类来为我的程序存储不同类型的数据(例如longchar*double,...)。

为了存储数据,我需要为每个表提供一个结构,如下所示:

struct MYSTRUCT0
{
    char variable0[10];
    char variable1[70];
};
struct MYSTRUCT1
{
    long variable0;
    long variable1;
    char variable2[6];
    double variable3;
};

但是每次我需要一个新表时它都会有很多工作,因为我需要编写一个函数来保存文件中的每个表,读取它等等。更糟糕的是,它并不是真正的面向对象。

所以我的问题是,有没有办法“浏览”结构来简化我的代码? 像这样:

for(int v=0; v<arraysize; v++)
for(int i=0; i<MYSTRUC0.length; i++)
{
    if (MYSTRUCT.getvar(i).type == long)
         DoSomethingForLong(myarray(v).getval(i));

    if (MYSTRUCT.getvar(i).type == char*)
         DoSomethingForCharPtr(myarray(v).getval(i));
}

我知道像这样的代码可以直接在C ++中工作。我只是用它来说明我的意思。

2 个答案:

答案 0 :(得分:1)

下面的代码只是一个例子,说明如何创建自己的“可变类型感知”结构,这可能是你想要的:

#include <vector>

enum MyTypes
{
    LONG,
    CHARPTR,
    DOUBLE,
} myTypes;

struct MyStruct
{
    MyStruct(long longVar)
    {
        variable.longVar = longVar;
        whichType = LONG;
    }

    MyStruct(char* charPtr)
    {
        variable.charPtr = charPtr;
        whichType = CHARPTR;
    }

    MyStruct(double var)
    {
        variable.var = var;
        whichType = DOUBLE;
    }

    ~MyStruct()
    {
    }

    MyTypes whichType;
    union {
        long longVar;
        char* charPtr;
        double var;
    } variable;
};

void DoSomethingForLong(MyStruct* doubleStruct)
{
    /*Do something specific to long*/
};

void DoSomethingForCharPtr(MyStruct* doubleStruct)
{
    /*Do something specific to char pointer*/
};

void DoSomethingForDouble(MyStruct* doubleStruct)
{
    /*Do something specific to double*/
};

int main()
{
    std::vector<MyStruct*> myVec;

    // add a struct with long variable
    long longVar = 2000000000;
    MyStruct* myLongStruct = new MyStruct(longVar);
    myVec.push_back(myLongStruct);

    // add a struct with char pointer
    char* charArray = new char[1000];
    MyStruct* myCharPtrStruct = new MyStruct(charArray);
    myVec.push_back(myCharPtrStruct);

    // add a struct with double variable
    double doubleVar = 200.200;
    MyStruct* myDoubleStruct = new MyStruct(doubleVar);
    myVec.push_back(myDoubleStruct);

    for (int i = 0; i < myVec.size(); ++i)
    {
        MyStruct* tempStruct = myVec[i];
        if (tempStruct->whichType == LONG)
        {
            DoSomethingForLong(tempStruct);
        }
        else if (tempStruct->whichType == CHARPTR)
        {
            DoSomethingForCharPtr(tempStruct);
        }
        else if (tempStruct->whichType == DOUBLE)
        {
            DoSomethingForDouble(tempStruct);
        }
    }

    if (myLongStruct)
    {
        delete myLongStruct;
        myLongStruct = nullptr;
    }

    if (myCharPtrStruct)
    {
        if (charArray)
        {
            delete[] charArray;
            charArray = nullptr;
        }

        delete myCharPtrStruct;
        myCharPtrStruct = nullptr;
    }

    if (myDoubleStruct)
    {
        delete myDoubleStruct;
        myDoubleStruct  = nullptr;
    }
}

答案 1 :(得分:1)

如果你去添加一个可以将数据成员导出为元组的成员函数,那么我们可以使用一些模板元编程来实现这个功能。

Live Demo(C ++ 14)

首先,改动:

struct MYSTRUCT0
{
    char variable0[10];
    char variable1[70];
    std::tuple<char(&)[10], char(&)[70]> GetData()
    {
        return std::tie(variable0, variable1);
    }
};
struct MYSTRUCT1
{
    long variable0;
    long variable1;
    char variable2[6];
    double variable3;
    std::tuple<long&, long&, char(&)[6], double&> GetData()
    {
        return std::tie(variable0, variable1, variable2, variable3);
    }
};

std::tie会将对这些成员的引用放入tuple

关于元组的好处是它将所有类型编码到我们可以利用的列表中。 (您可以编写宏来为您创建这些结构。)

从这里开始,策略是编写一个可以处理任何元组的函数。

由于我们通过调用std::get<i>来访问元组的元素,其中i是某个索引,我们需要一种方法来获取这些元素的索引,因此我们引入了一个间接层来创建它们使用std::index_sequence

template<class... T>
void ProcessData(const std::tuple<T...>& data){
    std::cout << "Processing " << sizeof...(T) << " data elements...\n";
    detail::ProcessDataImpl(data, std::make_index_sequence<sizeof...(T)>{});
}

detail::ProcessDataImpl的定义将使用一种称为简单包扩展的技术。这是一个技巧,我们利用数组初始化为参数包中的每个元素调用一个函数。它看起来有点奇怪,但请耐心等待:

template<class... T, size_t... I>
void ProcessDataImpl(const std::tuple<T...>& data, std::index_sequence<I...>){
    using swallow = int[];
    (void)swallow{0, (void(ProcessElement(std::get<I>(data))), 0)...};
}

这将为元组中的每个元素调用一个名为ProcessElement的函数。我们使用逗号运算符和void强制转换来确保函数没有真正做任何事情,我们所有的操作都只是为了它们的副作用(调用我们的ProcessElement函数)。

我们的ProcessElement函数将使用另一个间接层来传递参数,以便处理更复杂的类型,如字符数组。否则,我们可以为我们需要的类型重载它:

template<class T>
struct ProcessElementImpl
{
    static void apply(const T& element)
    {
        static_assert(sizeof(T) == 0, "No specialization created for T");
    }
};

template<size_t N>
struct ProcessElementImpl<char[N]>
{
    static void apply(const char(&arr)[N])
    {
        std::cout << "Process char array of size " << N << std::endl;
    }
};

template<class T>
void ProcessElement(const T& element)
{
    ProcessElementImpl<T>::apply(element);
}

void ProcessElement(long _element)
{
    std::cout << "Process a long\n";
}

void ProcessElement(double _element)
{
    std::cout << "Process a double\n";
}

请注意,我们为longdouble重载了,但我们将它传递给ProcessElementImpl我们的字符数组。这是必需的,因为我们不能部分地专门化模板函数,并且我们想要处理任意大小的数组。

基类模板还包含static_assert,因此我们强制编写专门用于导出数据类型。

最后我们可以这样称呼它:

int main()
{
    MYSTRUCT0 struct0;
    ProcessData(struct0.GetData());
    MYSTRUCT1 struct1;
    ProcessData(struct1.GetData());
    return 0;
}

输出:

Processing 2 data elements...
Process char array of size 10
Process char array of size 70
Processing 4 data elements...
Process a long
Process a long
Process char array of size 6
Process a double