I want a simple variant with
- minimal overhead
- that can be passed to functions written in the C language
so I decided to use a std::vector like this
typedef std::vector<char> SimpleVariant;
SimpleVariant data;
(1) store a std::string
for(std::string::iterator it = str.begin(); it != str.end(); ++it)
{
data.push_back( *it );
}
data.push_back('\0');
(2) store a double
data.resize(64);
std::sprintf(&data[0], "%.*g", 20, val);
(3) get a string
std::string str = std::s开发者_开发技巧tring( m_data.begin(), m_data.end() );
(4) get a double
double dbl = boost::lexical_cast<double>(&data[0]);
Is this a reasonable approach given my requirements? Is there lightweight variant that I can use instead of trying to reinvent the wheel?
I am aware of boost::variant and boost::any they are too heavyweight for my needsthat can be passed to functions written in the C language
No respectable variant in C++ is C-compatible, beyond passing a pointer to it as void* and providing functions that might manipulate it. C variants are guaranteed to be non-type-safe, whereas C++ variants have their types guaranteed. All you can do is store a type enumeration and a block of memory corresponding to the maximum size.
Also, boost::any
isn't more heavyweight than a vector
. You're just re-implementing it. In a less efficient way. And less safely.
Serializing to the textual representation of the data into dynamically allocated memory can hardly be considered minimal overhead. And then again, regarding the C functions, what exactly do you want to pass the variant to the C functions for, do you want just to pass the variant through? do you intend on using the data inside the C functions?
If you only want to pass the variant through C code (but not use it there), then consider using boost::variant or boost::any and passing void*
, if you intend on using the data inside C, I would recommend to use a structure with a tag to identify the type and an array big enough to hold the data you want to store and then cast the values into that array.
You haven't really given much information about how the C interface to this works. So this suggestion may not be helpful. But let's assume you have a C interface that looks like this:
int GetAValue(void *data, int size); //Returns the type of data stored in the data pointer. Will not write past size.
void DoSomethingWithValue(int type, void *data, int size); //Gives data to C, to do something with. Does not modify it.
void ModifyValue(int type, void *data, int size); //Gives data to C, where it modifies it.
I would use a boost::variant as follows:
typedef boost::variant<int, double, std::string> MyVariant;
struct DoSomethingWithValueVisit : public boost::static_visitor<>
{
void operator()(int val)
{
DoSomethingWithValue(TYPE_INTEGER, &val, sizeof(int));
}
void operator()(double val)
{
DoSomethingWithValue(TYPE_DOUBLE, &val, sizeof(double));
}
void operator()(const std::string &val)
{
DoSomethingWithValue(TYPE_STRING, (void*)val.c_str(), val.size() + 1);
}
};
struct ModifyValueVisit : public boost::static_visitor<>
{
void operator()(int &val)
{
ModifyValue(TYPE_INTEGER, &val, sizeof(int));
}
void operator()(double &val)
{
ModifyValue(TYPE_DOUBLE, &val, sizeof(double));
}
void operator()(std::string &val)
{
char buffer[128];
strncpy(buffer, val.c_str(), 127);
ModifyValue(TYPE_STRING, buffer, 128);
val = buffer;
}
};
MyVariant GetAValueFromC()
{
char buffer[128];
int type = GetAValue(buffer, 128);
switch(type)
{
case TYPE_INTEGER:
return *reinterpret_cast<int*>(&buffer[0]);
case TYPE_DOUBLE:
return *reinterpret_cast<double*>(&buffer[0]);
case TYPE_STRING:
return std::string(buffer);
}
}
int main()
{
MyVariant value = GetAValueFromC();
//Non-modifying
boost::apply_visitor(DoSomethingWithValueVisit(), value);
//Modifying
boost::apply_visitor(ModifyValueVisit(), value);
}
Feel free to add more types to the variant as needed.
精彩评论