Reputation: 67
As an exercise, I am designing a Goal-Oriented Action Planner in Unreal Engine 4. I am trying to setup my own structs which I can use to dynamically describe the world state using a variable-length list of data composed of individual variable types. I thought that variadic templated structs might be an interesting way to do this, but when implementing the value accessor I ran into problems.
I want it to work so that I can instantiate these structs at runtime, using FDataKeys<T>
in the generic parameters for FFact<T...>
. I then want to have a function GetValue<S>(string Key)
which will recursively check the Data keys until a match is found. I will implement a flag parameter to indicate the success or failure of a search, but I’m getting a compile error in my scratch code that doesn’t make sense to me.
I know that I could setup a helper class with a static function to do this, but I’m curious as to why this isn’t working and how I would be able to implement it this way if possible.
Error Message:
38:42: warning: ISO C++ forbids use of 'auto' in parameter declaration [-Wpedantic] In instantiation of 'S FFact<T, R ...>::GetValue(const string&) [with S = char; T = std::basic_string; R = {}; std::string = std::basic_string]':
50:45: recursively required from 'S FFact<T, R ...>::GetValue(const string&) [with S = char; T = bool; R = {std::basic_string<char, std::char_traits, std::allocator >}; std::string = std::basic_string]'
50:45: required from 'S FFact<T, R ...>::GetValue(const string&) [with S = char; T = char; R = {bool, std::basic_string<char, std::char_traits, std::allocator >}; std::string = std::basic_string]'
67:37: required from here
46:25: error: cannot convert 'std::basic_string' to 'char' in return In member function 'S FFact<T, R ...>::GetValue(const string&) [with S = char; T = std::basic_string; R = {}; std::string = std::basic_string]':
52:5: warning: control reaches end of non-void function [-Wreturn-type]
Code:
// FFact struct experiments
#include <iostream>
#include <string>
#include <vector>
using namespace std;
// Key-Value data struct
template<typename T>
struct FDataKey
{
FDataKey(string KeyStr, T Data)
{
Key = KeyStr;
Value = Data;
}
string Key;
T Value;
};
// Basic struct to enable generic recursion
template<typename ... T>
struct FFact
{
// GetValue's bottom of the barrel
// ToDo: Implement check that Key was not found
template<typename S>
S GetValue(string Key)
{
S defaultValue;
return defaultValue;
}
};
// Variadic templated container meant for FDataKey<> types only
template<typename T, typename ... R>
struct FFact<T, R...>
{
// Constructors
FFact(){}
FFact(const FDataKey<T>& FirstValue, const auto& ...Rest) : Data(FirstValue), Remainder(Rest...)
{}
// Recursively check Data members for FDataKeys with matching keys
template<typename S>
S GetValue(const string& Key)
{
if(Data.Key == Key)
{
return Data.Value;
}
else
{
return Remainder.GetValue<S>(Key);
}
}
// Member variables
FDataKey<T> Data;
FFact<R...> Remainder;
};
// Run tests
int main()
{
// Setup testing Fact
FFact<char, bool, string> f = FFact<char,bool,string>(
FDataKey<char>("Initial", 'w'),
FDataKey<bool>("Cake",false),
FDataKey<string>("Marco","Polo")
);
cout << f.GetValue<char>("Initial") << endl;
cout << f.GetValue<bool>("Cake") << endl;
cout << f.GetValue<string>("Marco") << endl;
}
Upvotes: 0
Views: 154
Reputation: 141040
Here you go, it compiles:
// FFact struct experiments
#include <iostream>
#include <string>
#include <vector>
#include <stdexcept>
#include <type_traits>
using namespace std;
// Key-Value data struct
template<typename T>
struct FDataKey {
FDataKey(string KeyStr, T Data) {
Key = KeyStr;
Value = Data;
}
string Key;
T Value;
};
// Basic struct to enable generic recursion
template<typename ... T>
struct FFact {
FFact(){}
// GetValue's bottom of the barrel
// ToDo: Implement check that Key was not found
template<typename S>
S GetValue(string Key) {
throw std::runtime_error("key not found");
S defaultValue;
return defaultValue;
}
};
// Variadic templated container meant for FDataKey<> types only
template<typename T, typename ...R>
struct FFact<T, R...> {
FFact(){}
// No need to use auto...
FFact(const FDataKey<T>& FirstValue, const FDataKey<R>&... Rest) :
Data(FirstValue), Remainder(Rest...)
{}
template<typename S>
S GetValue(const string& Key) {
if (Data.Key == Key) {
// you have to statically check
// if Data.Value can be converted to S
// if it can't, then... it can't.
if constexpr (std::is_convertible<T, S>::value) {
return Data.Value;
} else {
throw std::runtime_error("T is not convertiable to S");
}
} else {
// This is the first time I used this syntax, didn't knew about it.
return Remainder.template GetValue<S>(Key);
}
}
// Member variables
FDataKey<T> Data;
FFact<R...> Remainder;
};
// Run tests
int main()
{
// Setup testing Fact
FFact<char, bool, string> f = FFact<char,bool,string>(
FDataKey<char>("Initial", 'w'),
FDataKey<bool>("Cake",false),
FDataKey<string>("Marco","Polo")
);
cout << f.GetValue<char>("Initial") << endl;
cout << f.GetValue<bool>("Cake") << endl;
cout << f.GetValue<string>("Marco") << endl;
}
Upvotes: 1