How to use protobuf descriptor to read enums - c++

Let's say I have a .proto file with:
message Foo {
optional int32 x = 1;
enum y {
MOBILE = 0;
HOME = 1;
}
optional string z = 3;
}
Then I have this C++ code which prints all the types:
const Reflection *refl = Foo.GetReflection();
const Descriptor *desc = Foo.GetDescriptor();
int fieldCount = desc->field_count();
for(int i=0;i<fieldCount;i++){
const FieldDescriptor *field = desc->field(i);
cout << field->name().c_str() << " the type is "
<<field->type_name()<< ": Type Number "<< field->type() <<endl;
if(field->type()==FieldDescriptor::TYPE_ENUM){
//do something
}
The output is then:
x the type is int32: Type Number 5
z the type is string: Type Number 9
As seen in the output, the enum is skipped, how would I get the field descriptor to parse over the enum as well?

You do not have a field of your Enum type, you have only defined a type. Because of that, your iteration over fields doesn't yield anything related to the enum.
If you add a field of a given type, you will see your enum there.

Related

What is the safest way to cast a given value to enum in C++

I want to check if a given value belongs to MyEnum, which type is uint8. The given value could be uint8 or not, e.g., it could also be int, uint16, etc.
#include <iostream>
int main()
{
enum class MyEnum : std::uint8_t
{
A = 0,
B = 1,
C = 2,
D = 3,
};
int x = 256; // Note that this is not uint8
switch(MyEnum(x))
{
case MyEnum::A:
case MyEnum::B:
case MyEnum::C:
case MyEnum::D:
std::cout << "OK" << std::endl;
break;
default:
std::cout << "NOT OK" << std::endl;
}
return 0;
}
Output
OK
As you can see, 256 is casted to uint8_t 0, which is MyEnum::A.
My Question
What is the standard way in C++ to check if a given value belongs to an enum? Given the example above, is there any other way than just checking the value explicitly before casting?
if (x < 0 || x > 255)
{
// Do not cast to MyEnum
};
else
{
// Cast to MyEnum
}
You want to check std::numeric_limits<underlying_type_t<MyEnum>>::max(), which will be 255 for your std::uint8_t.
As for checking whether it matches one of the named values, this isn't possible. You cannot enumerate over names.
Note that even without an underlying type, any bitwise combination of enumerators is also valid, as is 0. Easiest to explain with an example: if you have {READ=1, WRITE=2, EXECUTE=4} then 1|2|4==7 is also in range.

Retrive Values From Enum

How can i recieve a specific value from an enum from a given index.
enum genre { Pop, Jazz, Classic}; //enum
struct album
{
string album_name;
genre kind;
int track_number;
string tracks[5];
string tracklocation;
};
void main()
{
album x1;
cout<<"Enter genre 0->pop, 1->Jazz, 2->Classic\n";
cin>>temp;
x1.kind=(genre)temp; // typecasting for int to enum
cout<<x1.kind<<endl;
}
When i run this code i just get the integer value i input , instead of the converted enum value
what i need is when user input 0,1 or 2 it needs to be converted using the enum to the relevant genre and saved in the stucture variable.
You can't put the identifiers (names) of an enum to cout. For example, the value of the name Pi in enum, suppose it's set to 3.14. You directly wants the user would type that value, it'll show the string Pi in output stream, but no, it's only within supposed within the code. Enumeration just holds the constants.
By default, your declaration enum {Pop, Jazz, Classic} holds the constant values 0, 1 and 2 respectively.
Rather, you could use arrayed string to get a value. Consider the following example:
struct album {
string album_name;
string genre[3] = {"Pop", "Jazz", "Classic"}; // this one holds 0 = "Pop" ...
int track_number;
string tracks[5];
string tracklocation;
};
And the driver code:
int main(void) {
album x1;
int temp;
std::cout << "Enter genre 0->pop, 1->Jazz, 2->Classic\n";
std::cin >> temp;
std::cout << x1.genre[temp] << endl;
// will display 0 = "Pop", 1 = "Jazz", 2 = "Classic"
return 0;
}

Easiest way to evaluate constant SVal

I'm trying to find the easiest way to get the value in a constant SVal. Currently I am doing something along the lines of:
SVal s = ...; // Get valid SVal from somewhere
int64_t val = 0; // Will hold value of SVal
if (!s.isUnknownOrUndef() && s.isConstant()) {
switch (s.getBaseKind()) {
case NonLocKind: {
s_val = s.getAs<nonloc::ConcreteInt>().getValue();
val = s_val.getValue().getExtValue();
}
// handle other cases
// ...
}
}
llvm::outs() << "Value is " << val << "\n";
I didn't show it here but there more code checking that values are known, checking the sign and type of constants, etc. I feel like there is probably a better way.

Calling C++ Method from Python using ctypes not working

I am trying to interact with a dll from python using ctypes, reading the documentation the C++ method signature is of the following:
my_c_pp_function(user *param1[],const int_8 param2,const int_8 values3[],const int_8 length_param1)
Essentially the c++ function requires a list of users, an integer,a list of values and the number of users, also an integer
Note: the users is a structure
containing name,age, and id. Something like:
typedef struct
{
char name[255];
int_16 age;
int_32 uid;
}user;
When I try calling this function from python code using ctypes I do:
def call_my_c_pp_function(list_of_users,int_param2,list_of_values,int_lenght_of_list):
myparam1=(ctypes.c_char_p * len(list_of_users))(*list_of_users)
myparam2=ctypes.c_int8(int_param2)
myparam3=(ctypes.c_int8 * len(list_of_values))(*list_of_values)
myparam4=ctypes.c_int8(int_lenght_of_list)
self.dll_object.my_c_pp_function.argtypes(ctypes.POINTER(ctypes.c_char_p),ctypes.c_int8,ctypes.POINTER(ctypes.c_int8),ctypes.c_int8)
ret_value=self.dll_object.my_c_pp_function(myparam1,myparam2,myparam3,myparam4)
Now every time I call the python function I get an error basically if the function succeeds the return value should be 0, any non zero number indicates some kind of problem.
I keep getting a large non-zero number as the return value.
What could I possibly be doing wrong? is the way I'm creating the array of users, and the array of values wrong?
I am not sure how to populate/make use of the c++ user structure in my python code so that my list is not just a list of strings but a list of users
I'm using Python2.7 on Windows
Regards
Assuming that you cannot change the signature of your library function.
I would recommend you to create a simple adapter for your c++ function, which just acts as an interface to your actual function.
This function would just take all the values you need and then convert them to your needed types.
Here is a working example (tested with python27). Note that I added an additional parameter for the number of elements in the int array.
py_caller.py
import ctypes
import os
class user(ctypes.Structure):
_fields_ = [("name", ctypes.c_char_p),
("age", ctypes.c_int),
("uid", ctypes.c_int)]
def create_users(count):
users = (user * count)()
for i in range(count):
users[i].name = ctypes.c_char_p("user" + str(i))
users[i].age = i
users[i].uid = i * i
return users
def create_values(count):
values = (ctypes.c_int * count)()
for i in range(count):
values[i] = i ** 2
return values
def main():
os.environ['PATH'] += ";" + os.path.dirname(os.path.abspath(__file__))
dll = ctypes.cdll.LoadLibrary('cppdll')
count = 4
users = create_users(count)
n_users = ctypes.c_int(count)
values = create_values(count)
n_values = ctypes.c_int(count)
some_int = ctypes.c_int(42)
res = dll.func(users, n_users, some_int, values, n_values)
print (res)
if __name__ == "__main__":
main()
export.h
#pragma once
typedef struct t_user
{
char *name;
int age;
int uid;
};
extern "C" __declspec(dllexport) int func(t_user *users, int n_users, int val, int *values, int n_values);
export.cpp
#include "export.h"
#include <iostream>
int func(t_user *users, int n_users, int val, int *values, int n_values)
{
std::cout << "users:\n";
for (auto i = 0; i < n_users; ++i)
{
std::cout << users[i].name
<< ": " << users[i].age
<< " - " << users[i].uid << "\n";
}
std::cout << "values:\n";
for (auto i = 0; i < n_values; ++i)
std::cout << values[i] << "\n";
std::cout << "some int: " << val << "\n";
// do something else with the values
// such as calling your actual library function
return 1;
}

C++ using 1 function for multiple varaibles. Only first run works

Newish to C++ but been researching alot so please bear with me.
I've been trying to use 1 function to relate 8 global double variables [ mA mB ... mG which ranges from values 1 to 10] to another double value.
I first obtain these 8 variables by obtaining the data from a csv file, throwing them to an array and then equating the set global variables. This all works fine. I cout the values correctly.
mA =10, mB=1 ,.... mG=2
However I need to use these variables to relate to another set value. So i Use:
double Function1(double Mvalue1)
{
if (Mvalue1 == 1) { double value = 10; return value ; }
if (Mvalue1 == 2) { double value = 20; return value ; }
.... // cont to 10 only increasing value by 10
if (Mvalue1 == 10) { double value = 110; return value ; }
}
void VarFunction()
{
mA2= Function1(mA); **//SHOULD output 110**
cout << "Vaule A " << mA2 << endl;
mB2= Function1(mB); **//SHOULD output 10**
cout << "Vaule B " << mB2 << endl;
....//.... 8 times
mG2 = Function1(mG); **//SHOULD output 20**
cout << "Vaule G " << mG2 << endl;
}
int main()
{
VarFunction()
return 0;
}
So the output i get here is
Value A 110
Value B -1.#IND
....
Value G -1.#IND
Why isnt the next call of function1 with the next variable not working?
In your code you have mA set to 12, but Function1 doesn't have a case for 12. So, I'm surprised you're even getting 110 printed for the first line. You aren't handling the case inside Function1 where Mvalue1 isn't one of the desired values, so this is the first thing to fix.
Also, assigning a number to a double and then returning it is unnecessarily complicated. A case statement would work well, assuming you really want to pass integers:
double Function1(int Mvalue1)
{
switch(Mvalue1) {
case 1: return 10;
case 2: return 20;
//...
case 10: return 110; // are you sure you don't want 100?
default: return -1; // handle the case here so that you always return a value.
}
}
Of course, if you really just want 10 times your input, why not:
double Function1(double mValue1)
{
return mValue1*10;
}
Not all paths in your function return a defined value, i.e. there's no return statement after all the conditionals.
The compiler is probably telling you that. If not - compile with higher warning level.
Use the std::map container when building relationships like this.
#include <iostream>
#include <map>
typedef std::map<double, double> rel_t;
int main()
{
rel_t mymap;
// You can directly
// std::map<double, double> mymap;
mymap[1] = 10;
mymap[2] = 20;
mymap[10] = 110;
std::cout << mymap[1] << std::endl; // Prints 10
std::cout << mymap[2] << std::endl; // Prints 20
std::cout << mymap[10] << std::endl; // Prints 110
}
This program seems to be working for me when I run it. However, I had to add declarations for mA2, mB2, and mG2 in your VarFunction(). You're also missing a semicolon after your call to VarFunction() in main().
I'd also suggest you return some default double in the function double Function(double Mvalue1), just in case Mvalue1 does not satisfy any of the if statements.
As already said, Function1() should return a value in case all if statements are false!
If the numbers your are dealing with have no fractional digits, use short, int, long or any other integer type for the variables. Then you can use a switch()/case construct or keep on using the comparison operator ==.
In case you must deal with floating point values, never use the == operator! When reading floating point values from text files (like CSV) or a database, a conversion from text to float/double is done. The result from such conversion can end in e.g. 9.999999999 or 10.000000001 instead of 10. And then the comparison with == is false!
To compare two double variables use a method like this:
bool dEqual( double dVal1, double dVal2, double dTolerance)
{
if( fabs( dVar1 - dVar2) < dTolerance) {
// dVar1 is nearly equal to dVar2
return true;
}
// dVar1 is not equal to dVar2
return false;
}
Then this comparison is true:
if( dEqual( 10.0, 9.999999998, 0.000001))
Apply a value for tolerance that meets the accuracy you need.