Can I “browse” structure elements to simplify the creation of a BDD class?

I create my own BDD class to store various data types (e.g. long , char* , double , ...) for my program.

To save the data, I need a structure for each table, for example:

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

But it works a lot every time I need a new table, because I need to write a function to save each table in a file, read it, etc. Worse, it is not object oriented.

So my question is: is there a way to “view” the structure in order to simplify my code? Something like that:

 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)); } 

I know that it is possible that such code works directly in C ++. I just use it to illustrate what I mean.

+5
source share
2 answers

The following is an example of how you can create your own structure with a variable type, which you may need:

 #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
source

If you are faced with the problem of adding a member function that can export your data members as a tuple, we can use template meta-programming to do this work.

Live Demo (C ++ 14)

First, change:

 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 puts references to these members in tuple .

The good thing about a tuple is that it encodes all types into a list, which we can use. (Perhaps you could write macros to create these structures for you.)

Hence the strategy is to write a function that can handle any tuples.

Since we access tuple elements with a call to std::get<i> , where i is some index, we need a way to get indices for these elements, so we introduce an indirectness level to create them using 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)>{}); } 

The definition of detail::ProcessDataImpl will use a method called a simple package extension. This is a trick in which we use array initialization to call a function for each element in the parameter package. It looks a little strange, but carrying with me:

 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)...}; } 

This will call a function named ProcessElement for each element in the tuple. We use the comma operator and void to ensure that the function does nothing, and all our operations are intended solely for their side effects (calling our ProcessElement function).

Our ProcessElement function will use another level of indirection to pass an argument to handle more complex types, such as our character arrays. Otherwise, we can overload it for the types that we need:

 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"; } 

Note that we overloaded for long and double , but we passed it to ProcessElementImpl for our character array. This is necessary because we cannot partially specialize the template function, and we want to process arrays of arbitrary size.

The base class template also contains static_assert , so we are forced to write specialization for exporting a data type.

Finally, we can call it this:

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

Output:

 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 
+1
source

Source: https://habr.com/ru/post/1272595/


All Articles