Cannot use std::string variable in RapidJSON function call - c++

I am all new to C++ and am running into an issue. I am using rapidJSON to create JSON documents.
void setKeyValue() {
Value obj(kObjectType);
Value key("key");
Value val(42);;
obj.AddMember(key, val, d.GetAllocator());
}
Works as expected. But when I try to replace the call to key to make it use a passed in param, like so:
void setKeyValue(string myKey) {
Value obj(kObjectType);
Value key(myKey);
Value val(42);;
obj.AddMember(key, val, d.GetAllocator());
}
The myKey in Value key(myKey) get a red curly underling in Visual Studio saying the following:
What is causing this and how can I solve it?

You don't get support for std::string by default. rapidJSON requires you to specify you want std::string support.
#define RAPIDJSON_HAS_STDSTRING 1
Only then is this constructor you're using valid:
GenericValue (const std::basic_string< Ch > &s, Allocator &allocator)

JSON library you are using seems that doesn't work with string objects from standard library, but it works with const char*.
So you must convert string object to char* with the method c_str():
void setKeyValue(string myKey) {
Value obj(kObjectType);
Value key((char*)myKey.c_str());
Value val(42);;
obj.AddMember(key, val, d.GetAllocator());
}

Related

How to replace #include <optional>

I come to you today with another question that my brain can't process by itself:
I got a cpp file that includes optional as a header file. Unfortunately, this works only on c++17 forwards, and I'm trying to compile it in c++14. This cpp file uses optional like this
std::optional<std::string> GetStringPropertyValueFromJson(const std::string& Property, const web::json::value& Json)
{
if (Json.has_field(utility::conversions::to_string_t(Property)))
{
auto& propertyValue = Json.at(utility::conversions::to_string_t(Property));
if (propertyValue.is_string())
{
return std::optional<std::string>{utility::conversions::to_utf8string(propertyValue.as_string())};
}
}
return std::nullopt;
}
and then the function is used to assign values like this:
std::string tokenType = GetStringPropertyValueFromJson("token_type", responseContent).value_or("");
std::string accessToken = GetStringPropertyValueFromJson("access_token", responseContent).value_or("");
Please help me with a proper substitution for OPTIONAL. Thanks and much love
PS: From what i've read, you can replace optional with pair somehow in order to get a similar result, but I don't really know how exactly.
PPS: I am new here so any tips on how to better write my questions or anything else are greatly appreciated :)
I guess in C++14 the optional header could be included by #include <experimental/optional>.
Change your method signature to
std::string GetStringPropertyValueFromJson(const std::string& Property, const web::json::value& Json)
and in the end just return the empty string
return "";
Then later in your code use it without std::optional::value_or:
std::string tokenType = GetStringPropertyValueFromJson("token_type", responseContent);
The logic is exactly the same and you don't use std::optional.
I see now your other question about possibility to use std::pair. Yes, you could also change your method to:
std::pair<std::string, bool> GetStringPropertyValueFromJson(const std::string& Property, const web::json::value& Json)
and return std::make_pair(valueFromJson, true) in case your json property has been found, or std::make_pair("", false) in case it was not. This also solves the problem with empty (but existing) json property.
A poor mans optional string that should be sufficient for your code is this:
struct my_nullopt {};
struct my_optional {
private:
std::string value;
bool has_value = false;
public:
my_optional(my_nullopt) {}
my_optional(const std::string& v) : value(v),has_value(true) {}
T value_or(const std::string& v) {
return has_value ? value : v;
}
};
Its a rather limited interface, for example it is not possible to set the value after construction. But it appears that you do not need that.
Alternatively you can use boost/optional.
Note that the tip you got about using a pair is just what I did above: The value and a bool. Just that std::pair is for cases where you cannot give better names than first and second (eg in generic code), but it is simple to provide a better interface than std::pair does here. With a pair the value_or would be something along the line of x.first ? x.second : "".
PS: Only in the end I realized that the code you present does not actually make use of what std::optional has to offer. As you are calling value_or(""), you cannot distinguish between a field with value "" or "" because the optional had no value. Because of that, the most simple solution is to use a plain std::string and return "" instead of std::nullopt.

How do I convert a std::string to System.String in C++ with Il2CppInspector?

I am using Il2CppInspector to generate scaffolding for a Unity game. I am able to convert System.String (app::String in Il2CppInspector) to std::string using the functions provided below.
How would I reverse this process; how do I convert a std::string to System.String?
helpers.cpp
// Helper function to convert Il2CppString to std::string
std::string il2cppi_to_string(Il2CppString* str) {
std::u16string u16(reinterpret_cast<const char16_t*>(str->chars));
return std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.to_bytes(u16);
}
// Helper function to convert System.String to std::string
std::string il2cppi_to_string(app::String* str) {
return il2cppi_to_string(reinterpret_cast<Il2CppString*>(str));
}
In short, I am looking for a function that takes in a std::string and returns an app::String
// Helper function to convert std::string to System.String
app::String string_to_il2cppi(std::string str) {
// Conversion code here
}
The accepted answer is actually wrong, there is no size parameter and copying stops at the first null byte (0x00) according to the MSDN documentation.
The following code fixes these problems and works correctly:
app::String* string_to_il2cppi(const std::string& string)
{
const auto encoding = (*app::Encoding__TypeInfo)->static_fields->utf8Encoding;
const auto managed_string = app::String_CreateStringFromEncoding((uint8_t*)&string.at(0), string.size(), encoding, nullptr);
return managed_string;
}
A quote from djkaty:
To create a string, you cannot use System.String‘s constructors –
these are redirected to icalls that throw exceptions. Instead, you
should use the internal Mono function String.CreateString. This
function has many overloads accepting various types of pointer and
array; an easy one to use accepts a uint16_t* to a Unicode string and
can be called as follows [...]
Export Il2CppInspector with all namespaces, which will give you access to Marshal_PtrToStringAnsi.
app::String* string_to_il2cppi(std::string str) {
return app::Marshal_PtrToStringAnsi((void*)&str, NULL);
}
Limitation: do not attempt to convert a string with null terminators inside of them example:
std::string test = "Hello\0world";
Use BullyWiiPlaza's solution if this is an issue for you.

Passing std::string_view to API execting const std::string&

I am using Socket.IO library to create a client. While sending a message I have to pass my message (data) as sio::message::list(msg)
void Socket_IO::send_message(std::string_view msg) //Gives Error
//void Socket_IO::send_message(const std::string &msg) //Works
{
this->client.socket()->emit(Socket_IO::general_message, sio::message::list(msg), [&](sio::message::list const& msg) {
});
}
class sio::message::list has a constructor
list(const string& text)
{
m_vector.push_back(string_message::create(text));
}
but does not have a std::string_view constructor
Error :
'<function-style-cast>': cannot convert from 'std::string_view' to 'sio::message::list'
I wish to know is their any way I can pass std::string_view for API expecting const std::string&
I can't use c_str() function as the string might contain binary data which may contain null character (0x00).
I was thinking of creating a string object sio::message::list(std::string str(msg)) but wish to inquire will it defeat the purpose of using std::string_view.
You could go with:
this->client.socket()->emit(Socket_IO::general_message, sio::message::list(std::string(msg)), ...
this makes the job done. It will initialize temporary string object and pass it to list constructor.

C++ nlohmann/json how to use runtime provided json_pointers to read json values

I am using the json parser Json for Modern C++ (https://github.com/nlohmann/json). I know that I can get the value of a JSON value with a JSON_Pointer:
auto v1 = j["/a/b/c"_json_pointer];
But how would I go about getting the value if the JSON Pointer is defined at runtime (passed into my function)?
std:string s1 = "/a/b/c";
auto v1 = j[s1]; // doesn't work
You can't append "json_pointer" to either the std::string assignment or to the s1 variable. Is there a function that will convert a std::string to a json_pointer? The caller knows nothing about json and can't have access to the "json.hpp" header. I've also tried
std::string s1 = "/a/b/c";
json_pointer p1(s1);
but "json_pointer" class is undefined. Other than this issue this is a great library that does everything else I need. TIA.
Look at the source code:
inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std::size_t)
{
return nlohmann::json::json_pointer(s);
}
If json_pointer is undefined, then you aren't using the right namespaces. Try
using nlohmann::json::json_pointer;
std::string s1 = "/a/b/c";
json_pointer p1(s1);

Accept null values for strings with rapidjson inside cereal, make them ""

For deserializing JSON into a c++ class, I'm using Cereal, which uses RapidJSON. As expected, c++ std::string can't have a null value. But other platforms do support null for strings (.NET SQL etc) and I get JSON from them with null values for strings. I need to tolerate this, and just make an empty string for nulls. What's the best way to do that?
I default to string substitute on the JSON changing nulls to "" like the following, but it is not a clean solution.
#include <cereal/archives/json.hpp>
#include <boost/algorithm/string.hpp>
// i.e. substitue all ":null with ":"" Like {"key":null} into {"key":""}
boost::replace_all(json, "\":null", "\":\"\"");
auto r = std::make_shared<R>();
std::stringstream ss(json);
{
cereal::JSONInputArchive archive(ss);
r->serialize(archive);
}
In case someone looks for this answer based on the exception generated by Cereal, it is: "rapidjson internal assertion failure: IsString()"
in cereal-1.1.2\include\cereal\external\rapidjson\document.h
change this
const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return data_.s.str; }
to this
const Ch* GetString() const { if (IsNull_()) return ""; RAPIDJSON_ASSERT(IsString()); return data_.s.str; }
I'm not proposing this should be changed in the cereal source because some people may want strict type checking like the original