I cast this any type
data: {
amount: 10,
product: {
goodsName: String,
barcode: String
}
}
to list,
So, I put the data
val detailsAsData: Map<String, Any> = menuDetailMapper.detailAsMap(data)
fun <T> T.serializeToMap(): Map<String, Any> {
return convert()
}
inline fun <I, reified O> I.convert(): O {
val json = gson.toJson(this)
return gson.fromJson(json, object : TypeToken<O>() {}.type)
}
In that result,
{amount=10.0, product={barcode=12377784597, goodsName=cheese}}
When transforming an any type variable, if there is a value of an any type, how can I transform it?
Like, product.
If I use the serializeToMap above, the value becomes Any type, but I want to know how to change it when the value is in the form of any object, not string or int.
Related
I want to have a map to save different types of value, so I have a map like
std::map<int, std::variant<int, std::string>> m ={{1,1},{2,"asd"}};
And Now I want to design a function to get the value by its key like
auto get(int key) {
...
return value;
}
That's to say what I want is like
get(1) -> 1
get(2) -> "asd"
So is it possible? If so what's the exact solution?
ADDED:
Yes, as for the purpose of the design, I want to save config datas reading from config files.
And after reading I need to make some type changing or data transform.
Such as in config files like
a=1
b=asd
And after reading it, I want to save it as
m["a"]=int(3) ---->refers a=1 and needs to plus 2 after reading its actual data 1
m["b"]=std::string("asdasd") ---->refers b=asd and needs to double it
So I think it will be easier if there is an interface function to get the exact value by the key
You cannot exactly design a function with this syntax. Just imagine this:
int key = 0;
if (rand() % 2) {
key = 1;
} else {
key = 2;
}
auto value = get(key);
Here's the riddle:
Please tell me the return type of get and the type of value. There can be only one answer and you can only answer with a single type that will remain unchanging no matter how much time I ask you this question.
If you can't answer, well, the compiler cannot either.
However, it doesn't mean you can't design something similar that will do what you need.
You could just return the variant. This might not be exactly what you're looking for, but it's worth noting since it doesn't change any of the input of the get function.
You can also just send the expected type:
get<int>(key)
The implementation would look like this:
template<typename T>
auto get(int key) -> T {
return std::get<int>(m[key]);
}
If you cannot know the expected type, then you could just send a visitor:
get(key, [](auto value) -> std::size_t {
if constexpr (std::same_as<int, decltype(value)>) {
// do stuff with value as an int since it's a int here
return value + 1;
} else {
// do stuff with value as a string since it's a string here
return value.size();
}
});
The implementation would look like this:
auto get(int key, auto visitor) -> decltype(auto) {
return std::visit(visitor, m[key]);
}
If I understand the requirements, you could return a proxy object from get that keeps a reference to the variant and depending on what you do with the proxy object, it can adapt. Example:
#include <iostream>
#include <variant>
#include <map>
#include <string>
struct Foo {
using variant = std::variant<int, std::string>;
// the proxy object
struct proxy {
// assignment:
template<class T>
variant& operator=(T&& value) {
*data = std::forward<T>(value);
return *data;
}
// for static_cast to the wanted type
explicit operator int& () { return std::get<int>(*data); }
explicit operator std::string& () { return std::get<std::string>(*data); }
variant* data;
};
proxy get(int key) {
// return the proxy object
return proxy{&m[key]};
}
std::map<int, std::variant<int, std::string>> m = {{1,1}, {2,"asd"}};
};
And it could be used like this:
int main() {
Foo f;
// you need to know what it stores:
std::cout << static_cast<int>(f.get(1)) << '\n';
std::cout << static_cast<std::string>(f.get(2)) << '\n';
// assigning is simple:
f.get(1) = "hello world"; // was int, now std::string
f.get(2) = 2; // was std::string, now int
std::cout << static_cast<std::string>(f.get(1)) << '\n';
std::cout << static_cast<int>(f.get(2)) << '\n';
}
Two possibilities:
A) You want to return either int or std::string depending on the index passed to the function.
Not possible. A function has one return type. Also auto is not magic. If you cannot write the actual type, then the compiler can't either.
B) You want to return the mapped_type of the map.
You can return std::variant<int, std::string> from the function.
Your get can't do much "better" than std::variant::get. With std::variant::get you need to specify at compile time what type you want to retrieve. And there is no way around that if you want to get either int or std::string.
There might be better ways to solve your actual issue, the one for which you thought auto get(int key) was the solution. A comment (by Eljay) mentions std::visit. There you can see a couple of examples of how to deal with variants.
There is a logging system with the number of attributes of arbitrary types. The attributes are added by external program(s) using public API (function template). The types aren't known beforehand. The typical way to print out an attribute's value looks like (simplified):
void print(logging::attribute_value const& attr)
{
auto val = logging::extract<int>(attr);
if (val) {
std::cout << "value: " << val.get() << std::endl;
}
}
In the example above it's already known that the attribute value type is int. What if the expected type isn't known? Of cource, I could write it like:
typedef boost::mpl::vector<int, std::string> types; // Expected types
logging::value_ref<types> val = logging::extract<types>(attr);
[..]
however in that case I have to define all possible types and handle them separately.
Is there a way to print out (or convert to strings) all values regardless of their types, assuming the types overload streaming operator?
UPDATE
Here are some more details:
// Add new attributes with arbitrary types
template<typename T>
void addProperty(const std::string &key, T && value)
{
auto attrs = boost::log::core::get()->get_thread_attributes();
attrs.erase(key);
attrs.insert(key, boost::log::attributes::make_constant(std::move(value)));
boost::log::core::get()->set_thread_attributes(attrs);
}
and another function that should print the properties' values
void printProperties()
{
const auto &attrs = boost::log::core::get()->get_thread_attributes();
for (auto const &a : attrs) {
// Print all values. Types aren't known.
}
}
Is there a way to print out (or convert to strings) all values regardless of their types, assuming the types overload streaming operator?
No, Boost.Log doesn't support that, you must know the name and type of the attribute value in order to be able to extract or visit it.
What you could do is maintain your own mapping between attribute names and formatting routines, such as this:
std::map<
logging::attribute_name,
std::function< void(std::ostream&, logging::attribute_value const&) >
> formatters;
template< typename T >
void formatter(std::ostream& strm, logging::attribute_value const& attr)
{
auto val = logging::extract< T >(attr);
if (val)
strm << val.get();
}
template<typename T>
void addProperty(const std::string &key, T && value)
{
typedef std::remove_cv_t< std::remove_reference_t< T > > value_type;
logging::attribute_name name(key);
formatters[name] = &formatter< value_type >;
boost::log::core::get()->add_thread_attribute(name,
boost::log::attributes::make_constant(std::forward< T >(value)));
}
void printProperties()
{
const auto &attrs = boost::log::core::get()->get_thread_attributes();
for (auto const &a : attrs) {
auto it = formatters.find(a.first);
if (it != formatters.end()) {
std::cout << "value: ";
it->second(std::cout, a.second.get_value());
std::cout << std::endl;
}
}
}
Of cource, I could write it like:
typedef boost::mpl::vector<int, std::string> types; // Expected types
logging::value_ref<types> val = logging::extract<types>(attr);
[..]
however in that case I have to define all possible types and handle
them separately.
Yes, this was going to be my first suggestion: consider making the type a sum type (a bound variant, where you can visit all the bound types).
Is there a way to print out (or convert to strings) all values
regardless of their types, assuming the types overload streaming
operator?
The canonical alternative is to use type erasure. Mind you, it does still have overhead, but replaces static polymorphism with dynamic polymorphism under the hood¹.
Have a look e.g. at https://www.boost.org/doc/libs/1_78_0/doc/html/boost/type_erasure/ostreamable.html
¹ which compilers in very specific circumstances and with proper hinting can sometimes devirtualize back to static invocations, but I digress
//type description example
class TextComponent_type : public _type
{
public:
using type = TextComponent;
static constexpr uInt value = 3;
};
//...
//ECSData member functions
template<uInt id>
auto& getVector()
{
return std::visit([this] (auto&& T_type)->(auto&) {
using T = typename std::decay_t<decltype(T_type)>::type;
return _getVectorFromT<T>();
}, dataMapping[id]);
}
template<typename T>
std::vector<T>& _getVectorFromT()
{
void* voidPtr = data[decltype(T::type)::value].get();
std::vector<T>* vec = static_cast<std::vector<T>*>(voidPtr);
return *vec;
}
In the code above I have a function meant to be called with a number, like getVector<3>(), the class with that member function has a std::map member variable called dataMapping which as Key has a number and as Value a std::variant holding simple structs that describe a type and the associated value (//type description example class on top of the code)
So I am visiting this variant to retrieve the associated type and passing it to _getVectorFromT() to be able to cast to the correct type and return it to the caller, in this example case the return would be a std::vector<TextComponent>&
But the code is not compiling at all, here the complete code if you want to try yourself https://godbolt.org/z/Tq32jm
So as the title says, can I return auto& from std::visit?
I need this because the return could be any of std::vector<RectComponent>&, std::vector<ConstraintComponent>&, std::vector<TextComponent>& etc...
The way you are trying to do this is not possible - the type must be inferred at compile time.
dataMapping[id] returns the same type - you could turn this part into a traits-like based design ala.
template<int id>
struct dataMapping;
template<>
struct dataMapping<0> {
using type = int; //... or whatever
}
//and so on
I'm using a template function searchByCriteria<T> where I would like to be able to run the function using both a string and a double. I have a list of custom-defined objects that have string and double attributes, and I would like to be able to use this function to check the criteria value (of whatever type, entered by the user), to check for matches in the object attributes of the same type.
I.e. User enters a double value, check the object collection for matching double values. User enters a string value, check the object collection for matching string values.
The problem I am having is once the value is entered, it is passed to another template function to be checked against the elements in the list. And at this point, the T object that is passed as a parameter, needs to be converted to either a double or a string to allow checking for matches.
Here is the code for this part:
//get a sub-list of transactions
//of all that match specified search criteria
template <typename T>
const TransactionList TransactionList::getTransactionsForSearchCriteria(T criteria) const {
//make a copy of list to avoid deleting existing data
TransactionList copy(*this);
//to have appropriate transactions added in
//then returned as copy
TransactionList ret;
//////////////////////////////////////////
//before checking for matches can start///
//must ID datatype of T instance//////////
//////////////////////////////////////////
//check all transactions until list empty
while (copy.size() > 0)
{
//check that criteria matches transaction attribute
if (/*converted criteria matches corresponding attribute*/)
{
//flag as match
}
}
}
As you can see, the parameter value criteria needs to be converted back into a specific data type before the while loop can be entered to check for matches. I am at a slight loss as to how to do this, as I am not aware of any casting methods in C++ that would be useful in this situation.
The only thing I can think of would be something like:
try
{
//example method
convertToDouble(criteria);
}
catch (SomeKindOfNumberException ex)
{
//cannot be a double
//so must be string
convertToString(criteria);
}
Any help is greatly appreciated.
Thanks,
Mark
How about something like this?
#include <iostream>
template<typename T>
T FuncB(T x) {
std::cout << "generic T ";
return x;
}
template<>
double FuncB<double>(double x) {
std::cout << "double ";
return 0.123;
}
template<>
std::string FuncB<std::string>(std::string x) {
std::cout << "string ";
return "xyz";
}
template <typename T>
void FuncA(T param) {
std::cout << "FuncA: ";
T tmp = FuncB(param);
std::cout << tmp << std::endl;
}
int main() {
std::string s = "abc";
FuncA(0.1);
FuncA(s);
FuncA(1);
}
FuncA can receive any T but it uses FuncB which is specialized for some specific types. If you want you can keep or delete FuncB(T) to support/avoid using unknown types.
The output of the previous code looks like:
FuncA: double 0.123
FuncA: string xyz
FuncA: generic T 1
Maybe you can add a Criterion<T> class holding user value and delegate the test to this class:
template<class T>
class Criterion {
T m_value;
public:
Criterion(T&& value) // allow implicit conversion
: m_value(std::forward<T>(value)) {}
bool appliesTo(const TransactionList &l); // provide default implementation if any
}
template<>
inline bool Criterion<double>::appliesTo(const TransactionList &l) {
/* check against double field */
}
template<>
inline bool Criterion<std::string>::appliesTo(const TransactionList &l) {
/* check against string field */
}
Then your algorithm will look like this:
template <typename T>
const TransactionList TransactionList::getTransactionsForSearchCriteria(Criterion<T> criteria) const {
TransactionList copy(*this);
TransactionList ret;
while (copy.size() > 0)
{
if (criteria.appliesTo(copy))
{
//flag as match
}
}
}
So your T object is a container, and you haven't given us a lot of information on it, but if you use vectors internally to the object to contain the objects you'll be way ahead.
Secondly if you were to use a pair to contain your vectors you could take advantage of get to select the correct member.
So, for the purposes of writing an answer my TransactionList object will look like this:
typedef pair<vector<string>, vector<double>> TransactionList;
Then we can rewrite your function:
template <typename T>
TransactionList getTransactionsForSearchCriteria(TransactionList result, T criteria) {
auto& attribute = get<vector<T>>(result);
auto it = find(attribute.begin(), attribute.end(), criteria);
//check that criteria matches transaction attribute
if(it != attribute.end()) {
//flag as match
}
//guessing what you want to do with this: attribute.insert(it, criteria); return result;
}
If you could ensure that the lists were maintained sorted, the standard provides binary search functionality which would dramatically improve your performance for large collections:
template <typename T>
transactionList getTransactionsForSearchCriteria(transactionList result, T criteria) {
auto& attribute = get<vector<T>>(result);
auto it = lower_bound(attribute.begin(), attribute.end(), criteria);
//check that criteria matches transaction attribute
if(it != attribute.end() && *it == criteria) {
//flag as match
}
//guessing what you want to do with this: attribute.insert(it, criteria); return result;
}
As far as determining if your inputted value is a string or a double when using TransactionList result, take all input as a string then see if you can consume the entire string successfully into a double: Forcing String to int Function to Consume Entire String
size_t size;
double num;
string i;
cin >> i;
if(sscanf(i.c_str(), "%lf%n", &num, &size) == 1 && size == i.size()) {
result = getTransactionsForSearchCriteria(result, num);
} else {
result = getTransaztionsForSearchCriteria(result, i);
}
Live Example
Im trying to figure out how to store my template, ill write a example:
class cDebugInfo
{
private:
DWORD * address;
string name;
template Type;
public:
Type GetFormattedValue()
{
return *(Type*)address;
}
cDebugInfo(){}
template <class T>
cDebugInfo(DWORD Address, string Name){
address = Address;
name = Name;
Type = T;
}
};
My goal, is to be able to add a item to my array:
std::vector<cDebugInfo>DebugItems;
template <class T>
void AddItem(std::string name, DWORD Address)
{
DebugItems.push_back(cDebugInfo(Address, name));
}
cDebugInfo* GetItemByNameP(std::string name)
{
for (int i = 0; i < DebugItems.size(); i++)
{
if (DebugItems[i].name == name)
{
return &DebugItems[i];
}
}
}
So i add the items to my array like this:
AddItem<int>(0x1337, "Test");
AddItem<string>(0x1337, "Test2");
And Therefore being able to call:
GetItemByName("Test")->GetFormattedValue();
And that should return the value in INT read form the given address since the template passed when i add the item, is a int. And ofcourse, the following should return the value stored in the address pointed by my pointer as a string:
GetItemByName("Test2")->GetFormattedValue();
I need it to "remember" what template was passed on to the class.
NOTE: Everything else works fine when i use GetItemByName with a template, but the thing is i dont know what template it is when i get them, only when i add them.
Thank you.
What you are asking for is not possible because every expression in C++ must have a known type at compile time. Consider this:
auto value = GetItemByName("BestItem")->GetFormattedValue();
GetItemByName(...) gives me a cDebugInfo*, but what does GetFormattedValue() give me? This must be the same type for every cDebugInfo* so that the above expression can be valid, so the type cannot withheld until runtime. So the general solution is impossible.
However, you can add specific solutions based on what it is you want to do. Let's say we just want to be able to print the formatted value. We can do that:
class cDebugInfo {
std::function<void()> printer; // type-erased functor
...
public:
template <class T>
cDebugInfo(DWORD Address, string Name){
address = Address;
name = Name;
printer = [this]{
std::cout << "Value as " << typeid(T).name() << ": "
<< *reinterpret_cast<T*>(address) << '\n';
};
}
};
This approach is called "type erasure". In this case, the lambda "stores" the type T, but the class itself just needs to know that it's a nullary function that returns void. We can expose that via:
void printValue() { printer(); }
So that:
GetItemByValue("BestItem")->printValue();
will print the value correctly based on the type it was constructed with.