0

In this and this question I talked about (and proposed a solution) to write a multi-function memoizator.

This is my solution:

template <typename ReturnType, typename... Args>
function<ReturnType(Args...)> memoize(function<ReturnType(Args...)> func)
{
    return ([=](Args... args) mutable {
        static map<tuple<Args...>, ReturnType> cache;
        tuple<Args...> t(args...);
        auto result = cache.insert(make_pair(t, ReturnType{}));
        if (result.second) {
            // insertion succeeded so the value wasn't cached already
            result.first->second = func(args...);
        }
        return result.first->second;
    });
}

struct MultiMemoizator
{
    map<string, boost::any> multiCache;
    template <typename ReturnType, typename... Args>
    void addFunction(string name, function < ReturnType(Args...)> func) {
        function < ReturnType(Args...)> cachedFunc = memoize(func);
        boost::any anyCachedFunc = cachedFunc;
        auto result = multiCache.insert(pair<string, boost::any>(name,anyCachedFunc));
        if (!result.second)
            cout << "ERROR: key " + name + " was already inserted" << endl;
    }
    template <typename ReturnType, typename... Args>
    ReturnType callFunction(string name, Args... args) {
        auto it = multiCache.find(name);
        if (it == multiCache.end())
            throw KeyNotFound(name);
        boost::any anyCachedFunc = it->second;
        function < ReturnType(Args...)> cachedFunc = boost::any_cast<function<ReturnType(Args...)>> (anyCachedFunc);
        return cachedFunc(args...);
    }
};

And this is a possible main:

int main()
{
    function<int(int)> intFun = [](int i) {return ++i; };
    function<string(string)> stringFun = [](string s) {
        return "Hello "+s;
    };
    MultiMemoizator mem;
    mem.addFunction("intFun",intFun);
    mem.addFunction("stringFun", stringFun);
    try
    {
        cout << mem.callFunction<int, int>("intFun", 1)<<endl;//print 2
        cout << mem.callFunction<string, string>("stringFun", " World!") << endl;//print Hello World!
        cout << mem.callFunction<string, string>("TrumpIsADickHead", " World!") << endl;//KeyNotFound thrown
    }
    catch (boost::bad_any_cast e)
    {
        cout << "Bad function calling: "<<e.what()<<endl;
        return 1;
    }
    catch (KeyNotFound e) 
    {
        cout << e.what()<<endl;
        return 1;
    }
}

To make it type safe at compile time, in this answer the author suggest to use a (for example) a Key<int(int)>. The problem with this solution is that two function with same signature cannot be inserted. So, as proposed in of the comments in this answer, we could enum each function to uniquely identify each function. Or we could add this enumeration inside the struct Key so we could preserve the type safety and uniquely identify each function.

THE PROBLEM:

I want to expand this solution so it can be used by different users. So if user1 defines for example some function int foo(int x) and memoize it in multiCache, it can write multiCache on file and user2 can use it for the same int foo(int x) (obviously it's responsability of user2 that foo is the same function used by user1).

The problem of the previous solution is that the enum defined by user1 for foo could not match by the one defined by user2 for the same function.

A POSSIBLE SOLUTION:

What about if instead of enum we could define a "probably unique" key for each function, like SHA(functionName)? Then we could write on file both multiCache object and a text file where we write (functionName,SHA(functioName)) and expose them to user2? Could be inefficient or unsafe (apart from the low probability of same sha code)?

Community
  • 1
  • 1
justHelloWorld
  • 6,478
  • 8
  • 58
  • 138

0 Answers0