Iterate over all the fields and get their values in protobuf message - c++

I have a dynamic protobuf message, and I don't know what fields this message contains.
What I want to do is, put all the values of all the fields into one string, for example, the message contains 2 fields, string name = "Jack"; and int age = 12;, the final result I want is "name:Jack, age:12".
Here is my idea, since I don't know what fields contained in this message, so I need to traverse the message to get all the fields' name, type (which can be accessed by Descriptor), and then get the value of each field, this is the most annoying part, because I need to write a long
switch (type) {
case TYPE_UINT32:
//call get_uint32
break;
case TYPE_UINT64:
//call get_uint64
break;
......
}
I wonder is there any other better idea to do this?

This is basically what Protobuf's own TextFormat class does:
https://github.com/google/protobuf/blob/master/src/google/protobuf/text_format.cc#L1473
You can use that code as an example when writing your own. It is indeed rather tedious, but there's really no better way to do it.

Message* message = &your_proto;
const google::protobuf::Descriptor* desc = message->GetDescriptor();
const google::protobuf::Reflection* ref = message->GetReflection();
for (int i = 0; i < desc->field_count(); ++i) {
const google::protobuf::FieldDescriptor* field_desc = desc->field(i);
switch (field_desc->cpp_type()) {
case google::protobuf::FieldDescriptor::CPPTYPE_INT32:
// call get_int32
break;
case google::protobuf::FieldDescriptor::CPPTYPE_INT64:
// call get_int64
break;
...
}
}

Related

How to virtual List control using a map data structure'?

I have a question while studying C++ MFC.
void AnchorDlg::OnGetdispinfoListctrl(NMHDR *pNMHDR, LRESULT *pResult)
{
NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
LV_ITEM* pItem = &(pDispInfo)->item;
CString str;
if(pItem == NULL) return;
int nRow = pItem->iItem;
int nCol = pItem->iSubItem;
if(nRow<0 || nRow >= mapShip->size()) return;
auto iter = mapShip->begin();
if(pItem->pszText)
{
switch(nCol)
{
case 1:
str.Format(_T("%.0f"), iter->second->mmsi);
//str.Format(_T("%.0f"), iter->second->mmsi);
lstrcpy(pItem->pszText, str);
break;
case 2:
str.Format(_T("%.7f"), iter->second->lat);
lstrcpy(pItem->pszText, str);
break;
case 3:
str.Format(_T("%.7f"), iter->second->lng);
lstrcpy(pItem->pszText, str);
break;
case 4:
str.Format(_T("%.1f"), iter->second->sog);
lstrcpy(pItem->pszText, str);
case 5:
str.Format(_T("%.1f"), iter->second->cog);
lstrcpy(pItem->pszText, str);
}
}
*pResult = 0;
}
mapShip consists of double and data objects.
I want to print out 1000 data, but only one data is printed.
I've used iter but only one data is output. Same data print repeat.
I must used map data structure.
I don't know how to use the map.
You don't seem to do anything with pItem->iItem (nRow), you just set it to the beginning of the list. You should instead search your data with this - requests may arrive in any random order. You don't have to iterate the list in OnGetdispinfoListctrl(), instead you should return the data to be displayed, given the iItem and iSubItem members. So consider using a more efficient structure to hold your data, if they are really too many (lists are not, because they are serial access structures, not random access ones). Also, don't copy the text into Item.pszText, instead set Item.pszText to point to your data. The if(pItem->pszText) check is not needed, neither is correct:
void AnchorDlg::OnGetdispinfoListctrl(NMHDR *pNMHDR, LRESULT *pResult)
{
NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
LV_ITEM* pItem = &(pDispInfo)->item;
// Persistent Buffer
static CString str;
if (pItem == NULL) return;
int nRow = pItem->iItem;
int nCol = pItem->iSubItem;
if (nRow < 0 || nRow >= mapShip->size()) return;
if (Item.mask & LVIF_TEXT) //Item/subItem text
{
//auto iter = mapShip->begin();
auto pValue = SearchMyData(nRow);
switch (nCol)
{
case 1:
str.Format(_T("%.0f"), pValue->mmsi);
break;
case 2:
str.Format(_T("%.7f"), pValue->lat);
break;
case 3:
str.Format(_T("%.7f"), pValue->lng);
break;
case 4:
str.Format(_T("%.1f"), pValue->sog);
break;
case 5:
str.Format(_T("%.1f"), pValue->cog);
break;
}
pItem->pszText = const_cast<LPTSTR>((LPCTSTR)str);
}
*pResult = 0;
}
EDIT:
Here is a excerpt from the documentation about the LV_ITEM structure:
pszText
If the structure specifies item attributes, pszText is a pointer to a null-terminated string containing the item text. When responding to an LVN_GETDISPINFO notification, be sure that this pointer remains valid until after the next notification has been received.
Also:
cchTextMax
This member is only used when the structure receives item attributes. ... It is read-only during LVN_GETDISPINFO and other LVN_ notifications.
And the example in the LVN_GETDISPINFO documentation does exactly this, ie sets the pszText pointer rather than copies text into it.
Also, you shouldn't get the conversion error if you are using either the multibyte (ANSI) or the wide (Unicode) version of both the LVITEM and CString, which you seem to do (you do not set them explicitly, which is OK, it defaults to T interpretation). It must be a const conflict. So, either use a const_cast:
pItem->pszText = const_cast<LPTSTR>((LPCTSTR)str);
or a char array instead:
static TCHAR _szItem[MAX_LEN]; // Persistent buffer
.
.
CString str;
str.Format(_T("%.0f"), iter->second->mmsi));
_tcscpy_s(_szItem, str);
pItem->pszText = _szItem;
break;
A virtual list-view control is a list-view control that doesn't store any data. Instead, users just tell the control how many items there are (by sending a LVM_SETITEMCOUNT message), and the control implementation will subsequently call back to the user to provide the data for the individual cells (by way of the LVN_GETDISPINFO message).
While the code in question is responding to the LVN_GETDISPINFO message, evaluating the nRow (and nCol) identifying the cell to be displayed, it doesn't use that data for information lookup. The code perpetually accesses the first item (mapShip->begin()) for display. However, it should be using nRow to index into the data represented by mapShip (see How to Use Virtual List-View Controls for an example).
Assuming that mapShip is an instantiation of the std::map class template, you'll want to index into that map using nRow as the index, e.g.:
auto const& value = mapShip[nRow];
and use value in place of iter. This will display items in the order they are stored in the associative container (sorted by key). If you need a different ordering, you'll have to map nRow onto the respective index in the map.

Convert a boring list of switch statements to something shorter and pleasing to look at?

I have a switch statement that runs like this
switch (abc) {
case FILE_0:
lf = m_a->olf[0];
kf = m_a->pkf[0];
break;
case FILE_1:
lf = m_a->olf[1];
kf = m_a->pkf[1];
break;
.
.
default:
LOG_ERR << "Wrong type to check";
return 0;
}
This happens about 30 times and i end up with 30 cases in this single switch.
Any way to shorten it in C++ 11 ? E.g. using templates.
Your code ain't that big to be sure about the intent, though, from what I can see in the snippet, you actually want to convert the symbolic value into an index. (Can I assume this is an enum?)
What I would do is to move that code into a separate function:
auto fileEnumToIndex(FileEnum file) {
switch (file) {
case FILE_0: return 0;
case FILE_1: return 1;
default: __builtin_unreachable();
}
}
Your code than changes to:
auto index = fileEnumToIndex(abc);
lf = m_a->olf[index];
kf = m_a->pkf[index];
If the FileEnum is a real enum, you can change the code in the function fileEnumToIndex to a simple static_cast
To cover the default case, you could return a std::optional and use the std::nullopt case to do some error handling. However, when FileEnum is an actual enum, I would assume error handling when you determine that value.
You can create a map of abc and the indice and use that for determining the indice.
// somewhere, maybe outside functions
static const std::unordered_map<abc_type, int> table = {
{FILE_0, 0},
{FILE_1, 1},
...
};
// inside function
auto idx_itr = table.find(abc);
if (idx_itr != table.end()) {
lf = m_a->olf[*idx_itr];
kf = m_a->pkf[*idx_itr];
} else {
// default case
}

How to ignore null objects when writing JSON with JsonCpp

According to the Google JSON style guide, it is advisable to remove empty or null values.
When using JsonCpp, how can empty or null values be removed, either from the object structure or when writing to a stream?
I want the following code:
#include <json/json.h>
#include <json/writer.h>
Json::Value json;
json["id"] = 4;
// The "name" property is an empty array.
json["name"] = Json::Value(Json::arrayValue);
Json::FastWriter fw;
std::cout << fw.write(json) << std::endl;
to produce:
{
"id": 4,
}
You may add a pre-process to remove empty members, something like:
void RemoveNullMember(Json::Value& node)
{
switch (node.type())
{
case Json::ValueType::nullValue: return;
case Json::ValueType::intValue: return;
case Json::ValueType::uintValue: return;
case Json::ValueType::realValue: return;
case Json::ValueType::stringValue: return;
case Json::ValueType::booleanValue: return;
case Json::ValueType::arrayValue:
{
for (auto &child : node)
{
RemoveNullMember(child);
}
return;
}
case Json::ValueType::objectValue:
{
for (const auto& key : node.getMemberNames())
{
auto& child = node[key]
if (child.empty()) // Possibly restrict to any of
// nullValue, arrayValue, objectValue
{
node.removeMember(key);
}
else
{
RemoveNullMember(node[key]);
}
}
return;
}
}
}
And so finally:
Json::Value json;
json["id"] = 4;
json["name"] = Json::Value(Json::arrayValue); // The "name" property is an empty array.
RemoveNullMember(json); // Or make a copy before.
Json::FastWriter fw;
std::cout << fw.write(json) << std::endl;
Personally I'd prefer an option in the writer which allows to filter out empty/null properties when writing. Thereby, one could define an own class like class MyFastWriter : public FastWriter, override printValue for handling type objectValue accordingly and call FastWriter::writeValue for the rest. Unfortunately, JsonCpp API has defined member function printValue as private, such that you cannot override it (and not even call it) from a custom derived class.
Hence, I see only three principal ways to achieve what you want: (1) Adapting the json value before writing, (2) defining an own writer class and copying a lot of code from FastWriter, or (3) change the source code of FastWriter.
There is already a proper answer for option (1) provided by Jarod42.
Option (2) and (3) share the major drawback that you copy or alter implementation details which might change in future versions of JsonCpp; But still, if one is very aware of the drawbacks coming with altering or copying a library's source code, it might be an option. A situation might be that the json value at hand shall keep empty properties, is very large, and has to be written rather often; then it becomes unhandy to copy the value, altering it just for writing, and writing it then again and again.
I'm for sure not a friend of altering source code; Anyway, see the following adapted version of FastWriter::writeValue which achieves the output you want:
void FastWriter::writeValue(const Value& value) {
switch (value.type()) {
// cases handling the other value.types remain as is...
...
// case handling objectValue is adapted:
case objectValue: {
Value::Members members(value.getMemberNames());
document_ += '{';
// inserted flag indicating that the first element is to be written:
bool isFirst = true;
for (Value::Members::iterator it = members.begin(); it != members.end();
++it) {
const std::string& name = *it;
// inserted to skip empty/null property values
if(value[name].empty() || value[name].isNull())
continue;
// Replaced: necessary because the first written entry is not necessarily members.begin:
// if (it != members.begin())
// document_ += ',';
if (!isFirst)
document_ += ',';
else
isFirst = false;
// Kept as is...
document_ += valueToQuotedStringN(name.data(), static_cast<unsigned>(name.length()));
document_ += yamlCompatiblityEnabled_ ? ": " : ":";
writeValue(value[name]);
}
document_ += '}';
} break;
}
}
I'm assuming that the values you're setting are not constant values and you're saving data from a class or some other data structure. In this case, you can simply check the data in C++ side and skip json["varName"] part completely.
Whatever you put in a JSON file is going to be in the final JSON because you're setting that field in the JSON file to something. As you said, it is advisable to not include NULL / empty values but it's not a must. NULL, empty or default values are still values that some people might want in their JSON file specifically to show that particular entity doesn't have that entry but it is still a field in that data.
I'd use the empty array as is for the name field in your JSON file, this way the reader can say that "Oh ok, this object doesn't have any name values to it" and if that wasn't supposed to happen or if it feels odd, even non-technical clients would be able to guide you through it and debugging would be much simpler. Unless this is a part of a networking code and you need to have the fastest response times. Otherwise, just include it.
Golden rule of debugging: Thich files save lives.

C++ indexed access to structure variables of different types

I have a number of bool and int variables, that I need to access by some sort of index. I found a way to do so, but it seems a bit too complicated. Is there a way to do this in more elegant way?
For now in header file I have structure
struct FDialogueFlags
{
bool tmpFlag1;
bool tmpFlag2;
// bool tmpFlagX;
int tmpState1;
int tmpState2;
// int tmpStateX;
};
and enumeration
enum class Eflags
{
// NAME = flag_ID,
TMP_FLAG_1 = 1,
TMP_FLAG_2 = 10,
// TMP_FLAG_X = XX,
TMP_STATE_1 = 101,
TMP_STATE_2 = 110,
// TMP_STATE_X = XXX,
};
And in source file I have function, that returns values for given index - flag_ID
int GetFlagValue(int flag_ID)
{
switch (Eflags(flag_ID))
{
case (Eflags::TMP_FLAG_1):
return flagsAndStates.tmpFlag1;
break;
case (Eflags::TMP_FLAG_2):
return flagsAndStates.tmpFlag2;
break;
/*case (Eflags::TMP_FLAG_X):
return flagsAndStates.tmpFlagX;
break;*/
case (Eflags::TMP_STATE_1):
return flagsAndStates.tmpState1;
break;
case (Eflags::TMP_STATE_2):
return flagsAndStates.tmpState2;
break;
/*case (Eflags::TMP_STATE_X):
return flagsAndStates.tmpStateX;
break;*/
default:
break;
}
}
That way everytime I need to add new "flag" or "state" I have to edit 3 places in my code.
Besides, if I need to access any "flag" or "state" by it's name written in enum, I can not do so, because Eflags (TMP_FLAG_1) returns TMP_FLAG_1 and not a number.
The other important thing, I'd like to keep variable namings, there might be up to hundred of this "flags" and "states" and it's crusial for them to have unique names and not just iterative numbers (Code above is just an example, in the end product I would name all of them according to their meaning)
If possible I'd like to avoid using std::map since it isn't supported natively by UE4.
So, the core idea is to access certain amount of variables of different numeric types by given number, possibly keeping variable naming.
Assuming you are asking for syntactic alternatives, Can you use tuples? Here - http://en.cppreference.com/w/cpp/utility/tuple
They are similar to structures and you can use std::get to retrieve values with index-based method. You can then use if statements for comparisons instead of switch statement.
Example -
#include <iostream>
#include <tuple>
int main()
{
auto t = std::make_tuple(1, true, 25);
std::cout << std::get<0>(t) << "\n"
<< std::get<1>(t) << "\n"
<< std::get<2>(t) << "\n";
}
IDEONE
I don't have any experience with UE4 and what native support means, but there are numerous drop-in alternatives to std::map like this one.

Reducing Condition statement for Large data

I am doing some sample programs in C++. I have 100,000 single column data in a Text File. I loaded that all data into a std::vector. Now I need to check the first character of every data whether it is '#' or '%' or '$'. The data are in Sorted manner in the way of '#','$','%'. I can do programming for this like Below.
for ( i = 0; i < myArray.size(); i++)
{
if(mrarray[i][0]=='#')
{
// do some process for '#' values
}
else if(mrarray[i][0]=='$')
{
// do some process for '$' values
}
else if(mrarray[i][0]=='%')
{
// do some process for '%' values
}
}
My Question is " Is it a best way to do this ? . Is there any other way to do this program with better efficiency ? " ???
That's about as efficient as it gets, the only thing that will make it a bit faster is using a switch statement.
for ( i = 0; i < myArray.size(); i++)
{
switch(myArray[i][0]){
case '#':
// Do stuff
break;
case '$':
// Do stuff
break;
case '%':
// Do stuff
break;
}
}
I'm also assuming you're only doing this once. If you're doing it more than once with the same data, then it can be made more efficient by sorting it. If that's the case, let me know and I will update my answer.
As state in why-is-processing-a-sorted-array-faster-than-an-unsorted-array, It may be more efficient to sort your lines first (based on first character) and then process your vector.
The switch statement proposed by David would be my choice.
But as alternative, you may try an array of function pointer, something like:
using my_function_pointer = void (*)(const std::string&/*, other required stuff*/);
const my_function_pointer funs[256] = { dummy_f, dummy_f, .., f_charp, f_dollar, f_mod, dummy_f, ..};
for ( i = 0; i < myArray.size(); i++) {
funs[myArray[i][0]](myArray[i] /*, other required stuff*/);
}
And anyway, you need to benchmark your change as for any optimization.