0

I'm reading "Modern C++ design" and hit on an idea to build a class that would act like a pointer but it would allocate object on the stack instead of on the heap. It could be used in functions that would normally return a pointer to an object allocated on the heap.

I will ask my questions before pasting the code:

  1. Is there something similar already?
  2. Has it a chance to be used? (if implemented more accurately of course)
  3. Why version that uses boost::mpl::max_element (commented out) doesn't work?
  4. How to call templated constructor if it had no parameter? (I mean: template <class U> StackPointer() { ... })?

Here is the code:

#include <iostream>

#include <boost/mpl/vector.hpp>
#include <boost/mpl/max_element.hpp>
#include <boost/mpl/empty.hpp>
#include <boost/mpl/pop_front.hpp>
#include <boost/mpl/size.hpp>
#include <boost/mpl/front.hpp>

template <class V, size_t VS=boost::mpl::size<V>::type::value>
struct max_size
{
    typedef typename boost::mpl::pop_front<V>::type subvector;
    typedef typename boost::mpl::front<V>::type front_type;
    static size_t const value = sizeof(front_type) > max_size<subvector>::value ?
                sizeof(front_type) : max_size<subvector>::value;
};

template <class V>
struct max_size<V, 0>
{
    static size_t const value = 0;
};

class StackPointerImplBase
{
public:
    virtual void clone(char const* from, char* to) const = 0;
};

template <class T>
class StackPointerImpl : public StackPointerImplBase
{
public:
    virtual void clone(char const* from, char *to) const
    {
        new(to) T(*reinterpret_cast<T const*>(from));
    }
};

template <class Base, class DerivedTypes>
class StackPointer
{
public:
    template <class T>
    StackPointer(T const& t)
    {
        std::cout << "Size of m_buf: "  << sizeof(m_buf) << std::endl;
        new(m_impl_buf) StackPointerImpl<T>();
        new(m_buf) T(t);
    }

    StackPointer(StackPointer const& sp)
    {
        //TODO: COPY m_impl_buf
        reinterpret_cast<StackPointerImplBase const*>(sp.m_impl_buf)->clone(sp.m_buf, m_buf);
    }

public:
    ~StackPointer()
    {
        get_pointer()->~Base();
    }

    Base* operator->()
    {
        return get_pointer();
    }

private:
    Base* get_pointer()
    {
        return reinterpret_cast<Base*>(m_buf);
    }

private:
    //typedef max_size<DerivedTypes> xxx_type;
    //typedef typename boost::mpl::max_element<DerivedTypes>::type::type biggest_type;
    //char m_buf[sizeof(typename boost::mpl::max_element<DerivedTypes>::type::type)];
    char m_buf[max_size<DerivedTypes>::value];
    char m_impl_buf[sizeof(StackPointerImplBase)];
};

class Shape
{
public:
    virtual ~Shape() {}

    virtual void say() const { std::cout << "I'm a shape" << std::endl; }
};

class Circle : public Shape
{
public:
    virtual void say() const { std::cout << "I'm a circle" << std::endl; }

private:
    float m_x;
    float m_y;
    float m_r;
};

class Line : public Shape
{
public:
    virtual void say() const { std::cout << "I'm a Line" << std::endl; }

private:
    float m_x1;
    float m_y1;
    float m_x2;
    float m_y2;
};


typedef StackPointer<Shape, boost::mpl::vector<Circle, Line> > ShapeStackPtr;

ShapeStackPtr read_shape()
{
    Line c;
    return ShapeStackPtr(c);
}


int main(int argc, char *argv[])
{

    {
        ShapeStackPtr shape = read_shape();
        shape->say();
    }

    return 0;
}
Luc Touraille
  • 79,925
  • 15
  • 92
  • 137
user2146414
  • 840
  • 11
  • 29
  • 1
    why don't you want to use the heap? – EHuhtala Mar 12 '13 at 18:42
  • In my opinion, a major issue with your approach is that all derived classes must be known beforehand. What if I want to add a new subclass to `Shape`? I would need to change all uses of `ShapeStackPtr`, or create a new type of `StackPointer`. If the pointer could be made to work with just the base class, this could be interesting, but I doubt this is possible. On another note, I think the issues of allocation performance are probably best handle by using custom allocators or by overriding new/delete. – Luc Touraille Apr 02 '13 at 09:35

2 Answers2

0

Boost optional is pretty similar to what you describe.

It uses pointer syntax so it can simulate what would be a NULL pointer in pointer-speak.

Drew Dormann
  • 59,987
  • 13
  • 123
  • 180
  • If I get it correctly boost optional allows you to return "NULL" or one type. My class tries to return an object that can handle many types that inherits from the Base type. – user2146414 Mar 07 '13 at 23:44
  • boost::any might be what you want – paulm Jan 05 '14 at 01:30
0

You can not allocate an object on the stack and then return anything that points to it. After you return from a function that stack frame is gone and it is not valid to point to that memory.

When you declare local variables in a function they are allocated on the stack then destroyed before the function return and the stack frame made obsolete.

So you can either allocate memory for objects on the heap and refer to them by pointers. Or have local variables in functions on the stack.

Or do you mean as a way to handle return values from called functions? But then you need to make a copy of the data from the heap to the stack anyway so why not just use T obj = *foo() if you need/want a copy on the stack.

To me it sounds like you are trying to complicate something that is already handled efficiently by the compiler.

AxelOmega
  • 974
  • 10
  • 18
  • In my example I cannot do T obj = *foo() because I do not know the returned type. I just know the base class. Normally the foo would return a pointer to the base class that points one of the inherited types allocated on the heap. You cannot do something similar allocating on he stack. In the second case your only choice is return by value. – user2146414 Mar 07 '13 at 23:42
  • So if a function returns a pointer to a heap allocated object. Then what? I still do not follow what you want to accomplish here. What exactly do you want to store on the stack? – AxelOmega Mar 07 '13 at 23:53
  • I want to avoid heap allocation this way. As a cheaper choice. – user2146414 Mar 07 '13 at 23:54
  • So where has the function returning a pointer allocated the memory for the object it is returning? – AxelOmega Mar 07 '13 at 23:56
  • My class uses a char buffer that size depends on the list of types that are passed as the second argument (the max size is choosen). Then the placement new operator is used to create an object. – user2146414 Mar 07 '13 at 23:59
  • 1
    I fail to see how this would gain anything. When you return from a function using StackPointer you copy the data in mbuf to a new StackPointer. Which is the same as copy a value. If you allocate your objects on the heap you just have to pass a pointer to it which is generally much faster. So instead of just allocating your object once on the heap you first. Create a char buffer on the called functions stack, then you copy all data from to the calling stack frame and you most likely copy more bytes then the size of the object. As I said you are trying to reinvent the wheel. – AxelOmega Mar 08 '13 at 00:22
  • In fact, when you return a StackPointer from a function then it is highly probable that the StackPointer won't be copied (if there is only one possible path in the function). So we could avoid heap allocation (that is slow - much slower than stack allocation). – user2146414 Mar 09 '13 at 00:32
  • That might be true. But it will be highly unpredictable from a reading the interface of a function. However you also have to ask yourself if heap allocation and return values are a bottleneck in your design. However the normal way of speeding up heap allocation if that is a problem is to roll out your own memory management code by overloading `new` and `delete` – AxelOmega Mar 09 '13 at 00:45