0

I can't get std::any running properly in the following peace of code. What I'd like to achieve is to return a reference/ pointer to the object hold by std::any. The version below using a void pointer is just running fine.

How can I translate that using std::any.

Why is std::any_cast designed to return a copy of the internal object anyway?

Thank you!

struct ReadOperation
{
  public:
    template <LeafnodeConcept L> 
    static bool visitLeafnode(L *l)
    {
        value = &(l->data);
        return false;
    }

    template <NodeLike N> static bool previsit(N* n)
    {
        return false;
    }

    template <class T> 
    static const T GetValue()
    {
        // return std::any_cast<T>(value);
        return *static_cast<T*>(value);
    }

    template<class T>
    static const T* GetValueRef()
    {
        return static_cast<T*>(value);
    }

  private:
    // inline static std::any value = nullptr;
    inline static void* value = nullptr;
};
andir
  • 59
  • 4
  • 1
    [`std::any_cast`](https://en.cppreference.com/w/cpp/utility/any/any_cast) can return a pointer – 463035818_is_not_an_ai Dec 13 '22 at 17:34
  • 1
    `std::any_cast` returns they type you tell it. If you say `T`, then you get a `T`. You need to say `T&` to get a reference or `T*` to get a pointer. – NathanOliver Dec 13 '22 at 17:34
  • 1
    `std::any_cast(&value)` returns a pointer to the contained value. – Raymond Chen Dec 13 '22 at 17:35
  • btw the code you posted fails to meet the requirement of a [mcve]. When I try to compile it I get a couple of compiler errors unrelated to the question. What compiler error or runtime error or undesired behavior you want to fix exactly one has to guess – 463035818_is_not_an_ai Dec 13 '22 at 17:36
  • Thanks @RaymondChen for pointing out the way to enforce returning a pointer to the internal value. – andir Dec 13 '22 at 18:09
  • @NathanOliver: What do you mean by telling std:.any_cast to say T& and T* get get reference or pointer, respectively? Something like std::any_cast(value) or std::any_cast(value)? – andir Dec 13 '22 at 18:14
  • @andir Yes. If you want to reference to `T` then you need `std::any_cast(value);` – NathanOliver Dec 13 '22 at 18:18
  • 1
    "So I created a sample which seems to work and the output is as expected." You could post an answer as answer, but you should not post the solution in the question. Then rather consider to delete the question, in case the issue is gone – 463035818_is_not_an_ai Dec 13 '22 at 18:31
  • @NathanOliver: Thank you, I added this to the answer. – andir Dec 13 '22 at 18:46

1 Answers1

0

Thank you for pointing out the right way to use std::any_cast in the comments. I created a sample which seems to work as expected.

#include <iostream>
#include <any>
#include <array>

struct ReadOperation
{
    template <class T>
    static void SetValue(T&& v) 
    {
        value = std::forward<T>(v);
    }
    
    template <class T> 
    static const T GetValue()
    {
        return std::any_cast<T>(value);
    }

    template<class T>
    static const T* GetValuePtr()
    {
        return std::any_cast<T>(&value);
    }
    
    template<class T>
    static const T& GetValueRef()
    {
        return std::any_cast<T&>(value);
    }

  private:
    inline static std::any value = nullptr;
};

int main()
{
    std::array<uint8_t, 12> arr { "hello world" }; 
    ReadOperation::SetValue(std::move(arr));
    
    // Returns a constant pointer
    const std::array<uint8_t, 12>* foo1 = ReadOperation::GetValuePtr<std::array<uint8_t, 12>>();
    std::cout << foo1->data() << std::endl;
    
    // Returns a constant reference 
    const std::array<uint8_t, 12>& foo2 = ReadOperation::GetValueRef<std::array<uint8_t, 12>>();
    std::cout << foo2.data() << std::endl;
    
    // Retruns a copy 
    const std::array<uint8_t, 12> foo3 = ReadOperation::GetValue<std::array<uint8_t, 12>>();
    std::cout << foo3.data() << std::endl;
    
    return 0;
}

But this is just half the truth. The original question was about how to imitate the use of a void pointer. And the solution is: Just use std::any exactly the way you'd use a void pointer.

It's straight forward: just wrap the pointer to your object to std::any, not the object itself. This will prevent it from creating a forwarded instance on the heap.

int main()
{
    std::array<uint8_t, 12> arr { "hello world" };
    std::cout << "Memory location: " << &arr << std::endl;
    
    // The new and better way
    std::any ptr1 = &arr;
    std::array<uint8_t, 12>* foo1 = std::any_cast<std::array<uint8_t, 12>*>(ptr1);
    std::cout << "Memory location: " << foo1 << std::endl;
    
    // The old-school but unsafe way
    void* ptr2 = &arr;
    std::array<uint8_t, 12>* foo2 = static_cast<std::array<uint8_t, 12>*>(ptr2);
    std::cout << "Memory location: " << foo2 << std::endl;  
}

Result: All memory locations should be the same as opposed to the previous example where you'll get a reference/pointer to the instance hold by the std::any value.

And finally, revisiting to the original questions on how to subsitute the void pointer by std::any we end up with:

struct ReadOperation
{
    template <LeafnodeConcept L> 
    static bool visitLeafnode(L *l)
    {
        value = &l->data;
        return false;
    }

    template <class T> 
    static const T GetValue()
    {
        //return *static_cast<T*>(value);
        return *std::any_cast<T*>(value);
    }

    template<class T>
    static const T* GetValueRef()
    {
        // return static_cast<T*>(value);
        return std::any_cast<T*>(value);
    }

  private:
    inline static std::any value = nullptr;
    //inline static void* value = nullptr;
};
andir
  • 59
  • 4