C++ Templated single value container (std::any) with meta data












-1














My intention is to implement a single value container which stores all possible types, like QVariant or others does. I tried to take advantage of std::any which gives a type safe container for the same purpose. Additionally I would like to store some meta data (timestamps mostly) which refer to target value.



I came up so far with two solutions which does not work properly. The class template version Value works but the interface does not fit properly in my opinion. See the first get function for an example. One need to know which type is returned on invocation. auto could help, but not in all situations.



The second solution has a pretty nice interface, like QVariant. See the access function get which returns LateValue. After calling this function one can dig deeper for the stored type and so on. Problem here is whenever I put this stuff into a library the template methods (getter and constructor) do not get instantiated. One can do this manually with the getter method but not with the constructor, it is simply not possible.



#include <any>
#include <memory>
#include <iostream>

class Time {
int m_a, m_b;
public:
Time(int a, int b) : m_a(a), m_b(b) {}
int a() const { return m_a; }
int b() const { return m_b; }

friend std::ostream &operator<<(std::ostream &os, const Time &time) noexcept {
os << time.a() << ' ' << time.b();
return os;
}
};

template<typename T>
class Value {
struct Impl {
std::any value;
};
std::unique_ptr<Impl> m_p;
public:
explicit Value(const T &value) : m_p(std::make_unique<Impl>()) {
m_p->value = std::make_any<T>(value);
}
T get() const {
return std::any_cast<T>(m_p->value);
}
};

class LateValue {
struct Impl {
std::any value;
};
std::unique_ptr<Impl> m_p;
public:
template<typename T>
LateValue(const T &value) : m_p(std::make_unique<Impl>()) {
m_p->value = std::make_any<T>(value);
}
template<typename T>
T get() const {
return std::any_cast<T>(m_p->value);
};




Here is a simple test program. Note that the above code goes into the same file.



template<typename T>
Value<T> get() {
return Value<T>(1.2f);
}

LateValue get() {
return LateValue(1.2f);
}

int main() {
Value<int> v0(1);
Value<float> v1(1.234f);
Value<Time> v2(Time(1, 2));

LateValue l0(1);
LateValue l1(1.234f);
LateValue l2(Time(1, 2));

std::cout << v0.get() << ' '
<< v1.get() << ' '
<< v2.get() << 'n'
<< l0.get<int>() << ' '
<< l1.get<float>() << ' '
<< l2.get<Time>() << 'n';

auto value = get<float>();
std::cout << " value is " << value.get() << 'n';

// l2.get<double>(); <--- exception: bad_any_cast (ok to me)

return 0;
}


The output is simply as in code, because it is working (single file!):



1 1.234 1 2
1 1.234 1 2
value is 1.2


So the question is: How to improve the implementation to have a nice interface with simplicity of template parameter? (I try to overcome the necessity to implement a get method for every supported type.)










share|improve this question









New contributor




maxik is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.




















  • This seems to be asking how to work around a specific language issue? I'm also not sure we have enough context about how Value and LateValue are actually used to comment on design.
    – user673679
    yesterday










  • Looks like QVariant is a poor mans std::any with the addition of a poor mans boost::lexecal_cast() built into the interface. To me it seems like you can implement a better version of QVariant with these two.
    – Martin York
    yesterday










  • Value and LateValue are identical, except that LateValue is missing a closing curly brace } near the end of the class. If you want them to do different things, you'll have to give them different code.
    – Quuxplusone
    1 hour ago
















-1














My intention is to implement a single value container which stores all possible types, like QVariant or others does. I tried to take advantage of std::any which gives a type safe container for the same purpose. Additionally I would like to store some meta data (timestamps mostly) which refer to target value.



I came up so far with two solutions which does not work properly. The class template version Value works but the interface does not fit properly in my opinion. See the first get function for an example. One need to know which type is returned on invocation. auto could help, but not in all situations.



The second solution has a pretty nice interface, like QVariant. See the access function get which returns LateValue. After calling this function one can dig deeper for the stored type and so on. Problem here is whenever I put this stuff into a library the template methods (getter and constructor) do not get instantiated. One can do this manually with the getter method but not with the constructor, it is simply not possible.



#include <any>
#include <memory>
#include <iostream>

class Time {
int m_a, m_b;
public:
Time(int a, int b) : m_a(a), m_b(b) {}
int a() const { return m_a; }
int b() const { return m_b; }

friend std::ostream &operator<<(std::ostream &os, const Time &time) noexcept {
os << time.a() << ' ' << time.b();
return os;
}
};

template<typename T>
class Value {
struct Impl {
std::any value;
};
std::unique_ptr<Impl> m_p;
public:
explicit Value(const T &value) : m_p(std::make_unique<Impl>()) {
m_p->value = std::make_any<T>(value);
}
T get() const {
return std::any_cast<T>(m_p->value);
}
};

class LateValue {
struct Impl {
std::any value;
};
std::unique_ptr<Impl> m_p;
public:
template<typename T>
LateValue(const T &value) : m_p(std::make_unique<Impl>()) {
m_p->value = std::make_any<T>(value);
}
template<typename T>
T get() const {
return std::any_cast<T>(m_p->value);
};




Here is a simple test program. Note that the above code goes into the same file.



template<typename T>
Value<T> get() {
return Value<T>(1.2f);
}

LateValue get() {
return LateValue(1.2f);
}

int main() {
Value<int> v0(1);
Value<float> v1(1.234f);
Value<Time> v2(Time(1, 2));

LateValue l0(1);
LateValue l1(1.234f);
LateValue l2(Time(1, 2));

std::cout << v0.get() << ' '
<< v1.get() << ' '
<< v2.get() << 'n'
<< l0.get<int>() << ' '
<< l1.get<float>() << ' '
<< l2.get<Time>() << 'n';

auto value = get<float>();
std::cout << " value is " << value.get() << 'n';

// l2.get<double>(); <--- exception: bad_any_cast (ok to me)

return 0;
}


The output is simply as in code, because it is working (single file!):



1 1.234 1 2
1 1.234 1 2
value is 1.2


So the question is: How to improve the implementation to have a nice interface with simplicity of template parameter? (I try to overcome the necessity to implement a get method for every supported type.)










share|improve this question









New contributor




maxik is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.




















  • This seems to be asking how to work around a specific language issue? I'm also not sure we have enough context about how Value and LateValue are actually used to comment on design.
    – user673679
    yesterday










  • Looks like QVariant is a poor mans std::any with the addition of a poor mans boost::lexecal_cast() built into the interface. To me it seems like you can implement a better version of QVariant with these two.
    – Martin York
    yesterday










  • Value and LateValue are identical, except that LateValue is missing a closing curly brace } near the end of the class. If you want them to do different things, you'll have to give them different code.
    – Quuxplusone
    1 hour ago














-1












-1








-1







My intention is to implement a single value container which stores all possible types, like QVariant or others does. I tried to take advantage of std::any which gives a type safe container for the same purpose. Additionally I would like to store some meta data (timestamps mostly) which refer to target value.



I came up so far with two solutions which does not work properly. The class template version Value works but the interface does not fit properly in my opinion. See the first get function for an example. One need to know which type is returned on invocation. auto could help, but not in all situations.



The second solution has a pretty nice interface, like QVariant. See the access function get which returns LateValue. After calling this function one can dig deeper for the stored type and so on. Problem here is whenever I put this stuff into a library the template methods (getter and constructor) do not get instantiated. One can do this manually with the getter method but not with the constructor, it is simply not possible.



#include <any>
#include <memory>
#include <iostream>

class Time {
int m_a, m_b;
public:
Time(int a, int b) : m_a(a), m_b(b) {}
int a() const { return m_a; }
int b() const { return m_b; }

friend std::ostream &operator<<(std::ostream &os, const Time &time) noexcept {
os << time.a() << ' ' << time.b();
return os;
}
};

template<typename T>
class Value {
struct Impl {
std::any value;
};
std::unique_ptr<Impl> m_p;
public:
explicit Value(const T &value) : m_p(std::make_unique<Impl>()) {
m_p->value = std::make_any<T>(value);
}
T get() const {
return std::any_cast<T>(m_p->value);
}
};

class LateValue {
struct Impl {
std::any value;
};
std::unique_ptr<Impl> m_p;
public:
template<typename T>
LateValue(const T &value) : m_p(std::make_unique<Impl>()) {
m_p->value = std::make_any<T>(value);
}
template<typename T>
T get() const {
return std::any_cast<T>(m_p->value);
};




Here is a simple test program. Note that the above code goes into the same file.



template<typename T>
Value<T> get() {
return Value<T>(1.2f);
}

LateValue get() {
return LateValue(1.2f);
}

int main() {
Value<int> v0(1);
Value<float> v1(1.234f);
Value<Time> v2(Time(1, 2));

LateValue l0(1);
LateValue l1(1.234f);
LateValue l2(Time(1, 2));

std::cout << v0.get() << ' '
<< v1.get() << ' '
<< v2.get() << 'n'
<< l0.get<int>() << ' '
<< l1.get<float>() << ' '
<< l2.get<Time>() << 'n';

auto value = get<float>();
std::cout << " value is " << value.get() << 'n';

// l2.get<double>(); <--- exception: bad_any_cast (ok to me)

return 0;
}


The output is simply as in code, because it is working (single file!):



1 1.234 1 2
1 1.234 1 2
value is 1.2


So the question is: How to improve the implementation to have a nice interface with simplicity of template parameter? (I try to overcome the necessity to implement a get method for every supported type.)










share|improve this question









New contributor




maxik is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.











My intention is to implement a single value container which stores all possible types, like QVariant or others does. I tried to take advantage of std::any which gives a type safe container for the same purpose. Additionally I would like to store some meta data (timestamps mostly) which refer to target value.



I came up so far with two solutions which does not work properly. The class template version Value works but the interface does not fit properly in my opinion. See the first get function for an example. One need to know which type is returned on invocation. auto could help, but not in all situations.



The second solution has a pretty nice interface, like QVariant. See the access function get which returns LateValue. After calling this function one can dig deeper for the stored type and so on. Problem here is whenever I put this stuff into a library the template methods (getter and constructor) do not get instantiated. One can do this manually with the getter method but not with the constructor, it is simply not possible.



#include <any>
#include <memory>
#include <iostream>

class Time {
int m_a, m_b;
public:
Time(int a, int b) : m_a(a), m_b(b) {}
int a() const { return m_a; }
int b() const { return m_b; }

friend std::ostream &operator<<(std::ostream &os, const Time &time) noexcept {
os << time.a() << ' ' << time.b();
return os;
}
};

template<typename T>
class Value {
struct Impl {
std::any value;
};
std::unique_ptr<Impl> m_p;
public:
explicit Value(const T &value) : m_p(std::make_unique<Impl>()) {
m_p->value = std::make_any<T>(value);
}
T get() const {
return std::any_cast<T>(m_p->value);
}
};

class LateValue {
struct Impl {
std::any value;
};
std::unique_ptr<Impl> m_p;
public:
template<typename T>
LateValue(const T &value) : m_p(std::make_unique<Impl>()) {
m_p->value = std::make_any<T>(value);
}
template<typename T>
T get() const {
return std::any_cast<T>(m_p->value);
};




Here is a simple test program. Note that the above code goes into the same file.



template<typename T>
Value<T> get() {
return Value<T>(1.2f);
}

LateValue get() {
return LateValue(1.2f);
}

int main() {
Value<int> v0(1);
Value<float> v1(1.234f);
Value<Time> v2(Time(1, 2));

LateValue l0(1);
LateValue l1(1.234f);
LateValue l2(Time(1, 2));

std::cout << v0.get() << ' '
<< v1.get() << ' '
<< v2.get() << 'n'
<< l0.get<int>() << ' '
<< l1.get<float>() << ' '
<< l2.get<Time>() << 'n';

auto value = get<float>();
std::cout << " value is " << value.get() << 'n';

// l2.get<double>(); <--- exception: bad_any_cast (ok to me)

return 0;
}


The output is simply as in code, because it is working (single file!):



1 1.234 1 2
1 1.234 1 2
value is 1.2


So the question is: How to improve the implementation to have a nice interface with simplicity of template parameter? (I try to overcome the necessity to implement a get method for every supported type.)







c++ template c++17






share|improve this question









New contributor




maxik is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.











share|improve this question









New contributor




maxik is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.









share|improve this question




share|improve this question








edited yesterday









user673679

2,3781925




2,3781925






New contributor




maxik is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.









asked yesterday









maxik

9913




9913




New contributor




maxik is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.





New contributor





maxik is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.






maxik is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.












  • This seems to be asking how to work around a specific language issue? I'm also not sure we have enough context about how Value and LateValue are actually used to comment on design.
    – user673679
    yesterday










  • Looks like QVariant is a poor mans std::any with the addition of a poor mans boost::lexecal_cast() built into the interface. To me it seems like you can implement a better version of QVariant with these two.
    – Martin York
    yesterday










  • Value and LateValue are identical, except that LateValue is missing a closing curly brace } near the end of the class. If you want them to do different things, you'll have to give them different code.
    – Quuxplusone
    1 hour ago


















  • This seems to be asking how to work around a specific language issue? I'm also not sure we have enough context about how Value and LateValue are actually used to comment on design.
    – user673679
    yesterday










  • Looks like QVariant is a poor mans std::any with the addition of a poor mans boost::lexecal_cast() built into the interface. To me it seems like you can implement a better version of QVariant with these two.
    – Martin York
    yesterday










  • Value and LateValue are identical, except that LateValue is missing a closing curly brace } near the end of the class. If you want them to do different things, you'll have to give them different code.
    – Quuxplusone
    1 hour ago
















This seems to be asking how to work around a specific language issue? I'm also not sure we have enough context about how Value and LateValue are actually used to comment on design.
– user673679
yesterday




This seems to be asking how to work around a specific language issue? I'm also not sure we have enough context about how Value and LateValue are actually used to comment on design.
– user673679
yesterday












Looks like QVariant is a poor mans std::any with the addition of a poor mans boost::lexecal_cast() built into the interface. To me it seems like you can implement a better version of QVariant with these two.
– Martin York
yesterday




Looks like QVariant is a poor mans std::any with the addition of a poor mans boost::lexecal_cast() built into the interface. To me it seems like you can implement a better version of QVariant with these two.
– Martin York
yesterday












Value and LateValue are identical, except that LateValue is missing a closing curly brace } near the end of the class. If you want them to do different things, you'll have to give them different code.
– Quuxplusone
1 hour ago




Value and LateValue are identical, except that LateValue is missing a closing curly brace } near the end of the class. If you want them to do different things, you'll have to give them different code.
– Quuxplusone
1 hour ago










1 Answer
1






active

oldest

votes


















0














So it looks like you want a type where you can add an object of any type. But you want the ability to pull that object out as any other type (with potentially a testing function that allows you to test the type).



class LokiValue
{
std::any value;

public:
template<typename T>
void put(T const& input) {
// Put stuff in value of type T
value = input;
}

template<typename S, typename T = S>
T get() {
// Get stuff in value that is of type S
// but return a value of type T
// by default S and T are the same type.
if (!value.has_value()) {
throw MyException("Message 1");
}
// This will throw if:
// * S is not the type stored.
// * If S is not convertible to T
return boost::lexical_cast<T>(std::any_cast<S>(value));
}

template<typename S, typename T>
bool check() {
// Check stuff in value is of type S and can be converted
// into an object of type T
if (!value.has_value()) {
return false;
}
try {
boost::lexical_cast<T>(std::any_cast<S>(value));
}
catch(...) {
return false;
}
return true;
}
};


Now this does not solve your problem directly as:



l2.get<double, Time>(); // This will still throw as Time is not convertable
// to a double yet. But we can simply change the
// the output format of time so it is compatable with
// the double stream input.


So what do we have to change to make them compatible?



class Time {
// STUFF
friend std::ostream &operator<<(std::ostream &os, const Time &time) noexcept {
return os << time.m_a << '.' << time.m_b;
// ^^^ Just change this to a dot
// The output of Time is compatible with
// the input of double,
}
};


So now we have:



int main() {
LokiValue l2;
l2.put(Time(1, 2)); // I was lazy and did not add a constructor.
// I'll leave that to you.

l2.get<double, Time>();
}





share|improve this answer





















    Your Answer





    StackExchange.ifUsing("editor", function () {
    return StackExchange.using("mathjaxEditing", function () {
    StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
    StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
    });
    });
    }, "mathjax-editing");

    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "196"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: false,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: null,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });






    maxik is a new contributor. Be nice, and check out our Code of Conduct.










    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f210866%2fc-templated-single-value-container-stdany-with-meta-data%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    0














    So it looks like you want a type where you can add an object of any type. But you want the ability to pull that object out as any other type (with potentially a testing function that allows you to test the type).



    class LokiValue
    {
    std::any value;

    public:
    template<typename T>
    void put(T const& input) {
    // Put stuff in value of type T
    value = input;
    }

    template<typename S, typename T = S>
    T get() {
    // Get stuff in value that is of type S
    // but return a value of type T
    // by default S and T are the same type.
    if (!value.has_value()) {
    throw MyException("Message 1");
    }
    // This will throw if:
    // * S is not the type stored.
    // * If S is not convertible to T
    return boost::lexical_cast<T>(std::any_cast<S>(value));
    }

    template<typename S, typename T>
    bool check() {
    // Check stuff in value is of type S and can be converted
    // into an object of type T
    if (!value.has_value()) {
    return false;
    }
    try {
    boost::lexical_cast<T>(std::any_cast<S>(value));
    }
    catch(...) {
    return false;
    }
    return true;
    }
    };


    Now this does not solve your problem directly as:



    l2.get<double, Time>(); // This will still throw as Time is not convertable
    // to a double yet. But we can simply change the
    // the output format of time so it is compatable with
    // the double stream input.


    So what do we have to change to make them compatible?



    class Time {
    // STUFF
    friend std::ostream &operator<<(std::ostream &os, const Time &time) noexcept {
    return os << time.m_a << '.' << time.m_b;
    // ^^^ Just change this to a dot
    // The output of Time is compatible with
    // the input of double,
    }
    };


    So now we have:



    int main() {
    LokiValue l2;
    l2.put(Time(1, 2)); // I was lazy and did not add a constructor.
    // I'll leave that to you.

    l2.get<double, Time>();
    }





    share|improve this answer


























      0














      So it looks like you want a type where you can add an object of any type. But you want the ability to pull that object out as any other type (with potentially a testing function that allows you to test the type).



      class LokiValue
      {
      std::any value;

      public:
      template<typename T>
      void put(T const& input) {
      // Put stuff in value of type T
      value = input;
      }

      template<typename S, typename T = S>
      T get() {
      // Get stuff in value that is of type S
      // but return a value of type T
      // by default S and T are the same type.
      if (!value.has_value()) {
      throw MyException("Message 1");
      }
      // This will throw if:
      // * S is not the type stored.
      // * If S is not convertible to T
      return boost::lexical_cast<T>(std::any_cast<S>(value));
      }

      template<typename S, typename T>
      bool check() {
      // Check stuff in value is of type S and can be converted
      // into an object of type T
      if (!value.has_value()) {
      return false;
      }
      try {
      boost::lexical_cast<T>(std::any_cast<S>(value));
      }
      catch(...) {
      return false;
      }
      return true;
      }
      };


      Now this does not solve your problem directly as:



      l2.get<double, Time>(); // This will still throw as Time is not convertable
      // to a double yet. But we can simply change the
      // the output format of time so it is compatable with
      // the double stream input.


      So what do we have to change to make them compatible?



      class Time {
      // STUFF
      friend std::ostream &operator<<(std::ostream &os, const Time &time) noexcept {
      return os << time.m_a << '.' << time.m_b;
      // ^^^ Just change this to a dot
      // The output of Time is compatible with
      // the input of double,
      }
      };


      So now we have:



      int main() {
      LokiValue l2;
      l2.put(Time(1, 2)); // I was lazy and did not add a constructor.
      // I'll leave that to you.

      l2.get<double, Time>();
      }





      share|improve this answer
























        0












        0








        0






        So it looks like you want a type where you can add an object of any type. But you want the ability to pull that object out as any other type (with potentially a testing function that allows you to test the type).



        class LokiValue
        {
        std::any value;

        public:
        template<typename T>
        void put(T const& input) {
        // Put stuff in value of type T
        value = input;
        }

        template<typename S, typename T = S>
        T get() {
        // Get stuff in value that is of type S
        // but return a value of type T
        // by default S and T are the same type.
        if (!value.has_value()) {
        throw MyException("Message 1");
        }
        // This will throw if:
        // * S is not the type stored.
        // * If S is not convertible to T
        return boost::lexical_cast<T>(std::any_cast<S>(value));
        }

        template<typename S, typename T>
        bool check() {
        // Check stuff in value is of type S and can be converted
        // into an object of type T
        if (!value.has_value()) {
        return false;
        }
        try {
        boost::lexical_cast<T>(std::any_cast<S>(value));
        }
        catch(...) {
        return false;
        }
        return true;
        }
        };


        Now this does not solve your problem directly as:



        l2.get<double, Time>(); // This will still throw as Time is not convertable
        // to a double yet. But we can simply change the
        // the output format of time so it is compatable with
        // the double stream input.


        So what do we have to change to make them compatible?



        class Time {
        // STUFF
        friend std::ostream &operator<<(std::ostream &os, const Time &time) noexcept {
        return os << time.m_a << '.' << time.m_b;
        // ^^^ Just change this to a dot
        // The output of Time is compatible with
        // the input of double,
        }
        };


        So now we have:



        int main() {
        LokiValue l2;
        l2.put(Time(1, 2)); // I was lazy and did not add a constructor.
        // I'll leave that to you.

        l2.get<double, Time>();
        }





        share|improve this answer












        So it looks like you want a type where you can add an object of any type. But you want the ability to pull that object out as any other type (with potentially a testing function that allows you to test the type).



        class LokiValue
        {
        std::any value;

        public:
        template<typename T>
        void put(T const& input) {
        // Put stuff in value of type T
        value = input;
        }

        template<typename S, typename T = S>
        T get() {
        // Get stuff in value that is of type S
        // but return a value of type T
        // by default S and T are the same type.
        if (!value.has_value()) {
        throw MyException("Message 1");
        }
        // This will throw if:
        // * S is not the type stored.
        // * If S is not convertible to T
        return boost::lexical_cast<T>(std::any_cast<S>(value));
        }

        template<typename S, typename T>
        bool check() {
        // Check stuff in value is of type S and can be converted
        // into an object of type T
        if (!value.has_value()) {
        return false;
        }
        try {
        boost::lexical_cast<T>(std::any_cast<S>(value));
        }
        catch(...) {
        return false;
        }
        return true;
        }
        };


        Now this does not solve your problem directly as:



        l2.get<double, Time>(); // This will still throw as Time is not convertable
        // to a double yet. But we can simply change the
        // the output format of time so it is compatable with
        // the double stream input.


        So what do we have to change to make them compatible?



        class Time {
        // STUFF
        friend std::ostream &operator<<(std::ostream &os, const Time &time) noexcept {
        return os << time.m_a << '.' << time.m_b;
        // ^^^ Just change this to a dot
        // The output of Time is compatible with
        // the input of double,
        }
        };


        So now we have:



        int main() {
        LokiValue l2;
        l2.put(Time(1, 2)); // I was lazy and did not add a constructor.
        // I'll leave that to you.

        l2.get<double, Time>();
        }






        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered yesterday









        Martin York

        72.7k483261




        72.7k483261






















            maxik is a new contributor. Be nice, and check out our Code of Conduct.










            draft saved

            draft discarded


















            maxik is a new contributor. Be nice, and check out our Code of Conduct.













            maxik is a new contributor. Be nice, and check out our Code of Conduct.












            maxik is a new contributor. Be nice, and check out our Code of Conduct.
















            Thanks for contributing an answer to Code Review Stack Exchange!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            Use MathJax to format equations. MathJax reference.


            To learn more, see our tips on writing great answers.





            Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


            Please pay close attention to the following guidance:


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f210866%2fc-templated-single-value-container-stdany-with-meta-data%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            How to reconfigure Docker Trusted Registry 2.x.x to use CEPH FS mount instead of NFS and other traditional...

            is 'sed' thread safe

            How to make a Squid Proxy server?