0

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;
}
3
  • 1
    return Data.Value; - You do return Data.Value; from each recursive call, even when you return an S. Ex. in GetValue<char>. C++ is statically typed, each Data.Value has to be convertible to char then. Commented Feb 21, 2021 at 9:57
  • I guess that’s what is confusing to me. Since Data.Value should not be returned until the Key matches. Thus, the only type conversion should be S to typeof(Data.Value) which should only throw an error when the incorrect generic types are passed in the call. Commented Feb 21, 2021 at 10:09
  • should not be returned it will not be returned, but statically all paths have to be valid. Like int func() { if (0) { return std::string{}; } return 5; } will not compile, no matter string is "never returned". Commented Feb 21, 2021 at 10:19

1 Answer 1

1

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;
}
answered Feb 21, 2021 at 10:18
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.