Redirecting to the constructor in place

I have a message class that was previously a little sick to work with, you had to create a message class, tell it to allocate space for your object, and then fill the space either line by line or in order.

I want to create a message object using the immediate built-in new result object, but do it using simple syntax on the call site, while providing a copy.

#include <cstdint>

typedef uint8_t id_t;
enum class MessageID { WorldPeace };

class Message
{
    uint8_t* m_data;         // current memory
    uint8_t m_localData[64]; // upto 64 bytes.
    id_t m_messageId;
    size_t m_size; // amount of data used
    size_t m_capacity; // amount of space available
    // ...

public:
    Message(size_t requestSize, id_t messageId)
        : m_data(m_localData)
        , m_messageId(messageId)
        , m_size(0), m_capacity(sizeof(m_localData))
    {
        grow(requestSize);
    }

    void grow(size_t newSize)
    {
        if (newSize > m_capacity)
        {
            m_data = realloc((m_data == m_localData) ? nullptr : m_data, newSize);
            assert(m_data != nullptr); // my system uses less brutal mem mgmt
            m_size = newSize;
        }
    }

    template<typename T>
    T* allocatePtr()
    {
        size_t offset = size;
        grow(offset + sizeof(T));
        return (T*)(m_data + offset);
    }

#ifdef USE_CPP11
    template<typename T, typename Args...>
    Message(id_t messageId, Args&&... args)
        : Message(sizeof(T), messageID)
    {
        // we know m_data points to a large enough buffer
        new ((T*)m_data) T (std::forward<Args>(args)...);
    }
#endif
};

Pre-C ++ 11 I had a nasty CONSTRUCT_IN_PLACE macro that did:

#define CONSTRUCT_IN_PLACE(Message, Typename, ...) \
    new ((Message).allocatePtr<Typename>()) Typename (__VA_ARGS__)

And you would say:

Message outgoing(sizeof(MyStruct), MessageID::WorldPeace);
CONSTRUCT_IN_PLACE(outgoing, MyStruct, wpArg1, wpArg2, wpArg3);

With C ++ 11 you should use

Message outgoing<MyStruct>(MessageID::WorldPeace, wpArg1, wpArg2, wpArg3);

But I find it dirty. I want to implement the following:

    template<typename T>
    Message(id_t messageId, T&& src)
        : Message(sizeof(T), messageID)
    {
        // we know m_data points to a large enough buffer
        new ((T*)m_data) T (src);
    }

For the user to use

Message outgoing(MessageID::WorldPeace, MyStruct(wpArg1, wpArg2, wpArg3));

But it seems that at first it creates a temporary one MyStructon the stack, turning the in-place newinto a call to the move constructor T.

, POD, :

void dispatchWorldPeace(int wpArg1, int wpArg2, int wpArg3)
{
    Message outgoing(MessageID::WorldPeace, MyStruct(wpArg1, wpArg2, wpArg3));
    outgoing.send(g_listener);
}

, /.

, new.

, , ? (GCC 4.8.1, Clang 3.5, MSVC 2013)

+4
2

/ : copy elision , , . , ( , , ), ( 12.8 [ class.copy] 31: , / , . ). [] new , , ( ), / . std::forward<T>(...) , / :

template<typename T>
Message(id_t messageId, T&& src)
    : Message(sizeof(T), messageID)
{
    // placement new take a void* anyway, i.e., no need to cast
    new (m_data) T (std::forward<T>(src));
}

, . , , , , , , / :

template <typename>
struct Tag {};

template <typename T, typename A>
Message::Message(Tag<T>, id_t messageId, A... args)
    : Message(messageId, sizeof(T)) {
    new(this->m_data) T(std::forward<A>(args)...);
}

, , - id_t , , :

typedef uint8_t id_t;
template <typename T, id_t id> struct Tag {};
struct MessageId {
    static constexpr Tag<MyStruct, 1> WorldPeace;
    // ...
};
template <typename T, id_t id, typename... A>
Message::Message(Tag<T, id>, A&&... args)
    Message(id, sizeof(T)) {
    new(this->m_data) T(std::forward<A>)(args)...);
}
+3

, ++ 2049 , , , .

, ++, - . ( , ).

, , , ( ), (, , ).

. , , , , , , / .

, , , ,

  • -, ,
  • , ,
  • -.

toplevel, -.

, , .

, , ?

API, . - -, , .

" " , . , , .

, , .

, , - (.. , ), - , .

,

-, , , .

, ( ) .
.

#include <cstdint>
#include <cstdio>
#include <new>

typedef uint8_t id_t;
enum class MessageID { WorldPeace, Armaggedon };

#define SMALL_BUF_SIZE 64

class Message {
    id_t     m_messageId;
    uint8_t* m_data;
    uint8_t  m_localData[SMALL_BUF_SIZE];

public:

    // choose the proper location for contents
    Message (MessageID messageId, size_t size)
    {
        m_messageId = (id_t)messageId;
        m_data = size <= SMALL_BUF_SIZE ? m_localData : new uint8_t[size];
    }

    // dispose of the contents if need be
    ~Message ()
    {
        if (m_data != m_localData) delete m_data;
    }

    // let placement new know about the contents location
    void * location (void)
    {
        return m_data;
    }
};

// a macro to do the in-place construction
#define BuildMessage(msg, id, obj, ...   )       \
        Message msg(MessageID::id, sizeof(obj)); \
        new (msg.location()) obj (__VA_ARGS__);  \

// example uses
struct small {
    int a, b, c;
    small (int a, int b, int c) :a(a),b(b),c(c) {}
};
struct big {
    int lump[1000];
};

int main(void)
{
    BuildMessage(msg1, WorldPeace, small, 1, 2, 3)
    BuildMessage(msg2, Armaggedon, big)
}

.

, .

, , - 64 , , .

, , , .

, && . , , .

, , && , .

Message, , , : , - .

Message, , , .

, , , .

, .

, , , , -.

.

, -.

, . , - .

, DMA, , .

- .

#include <cstdint>
#include <new>

// ==========================================================================
// Common definitions
// ==========================================================================

// message header
enum class MessageID : uint8_t { WorldPeace, Armaggedon };
struct MessageHeader {
    MessageID id;
    uint8_t   __padding; // one free byte here
    uint16_t  size;
};

// small buffer size
#define SMALL_BUF_SIZE 64

// dummy send function
int some_DMA_trick(int destination, void * data, uint16_t size);

// ==========================================================================
// Macro solution
// ==========================================================================

// -----------------------------------------
// Message class
// -----------------------------------------
class mMessage {
    // local storage defined even for big messages
    MessageHeader   m_header;
    uint8_t         m_localData[SMALL_BUF_SIZE];

    // pointer to the actual message
    MessageHeader * m_head;
public:  
    // choose the proper location for contents
    mMessage (MessageID messageId, uint16_t size)
    {
        m_head = size <= SMALL_BUF_SIZE 
            ? &m_header
            : (MessageHeader *) new uint8_t[size + sizeof (m_header)];
        m_head->id   = messageId;
        m_head->size = size;
   }

    // dispose of the contents if need be
    ~mMessage ()
    {
        if (m_head != &m_header) delete m_head;
    }

    // let placement new know about the contents location
    void * location (void)
    {
        return m_head+1;
    }

    // send a message
    int send(int destination)
    {
        return some_DMA_trick (destination, m_head, (uint16_t)(m_head->size + sizeof (m_head)));
    }
};

// -----------------------------------------
// macro to do the in-place construction
// -----------------------------------------
#define BuildMessage(msg, obj, id, ...   )       \
        mMessage msg (MessageID::id, sizeof(obj)); \
        new (msg.location()) obj (__VA_ARGS__);  \

// ==========================================================================
// Template solution
// ==========================================================================
#include <utility>

// -----------------------------------------
// template to check storage capacity
// -----------------------------------------
template<typename T>
struct storage
{
    enum { local = sizeof(T)<=SMALL_BUF_SIZE };
};

// -----------------------------------------
// base message class
// -----------------------------------------
class tMessage {
protected:
    MessageHeader * m_head;
    tMessage(MessageHeader * head, MessageID id, uint16_t size) 
        : m_head(head)
    {
        m_head->id = id;
        m_head->size = size;
    }
public:
    int send(int destination)
    {
        return some_DMA_trick (destination, m_head, (uint16_t)(m_head->size + sizeof (*m_head)));
    }
};

// -----------------------------------------
// general message template
// -----------------------------------------
template<bool local_storage, typename message_contents>
class aMessage {};

// -----------------------------------------
// specialization for big messages
// -----------------------------------------
template<typename T>
class aMessage<false, T> : public tMessage
{
public:
    // in-place constructor
    template<class... Args>
    aMessage(MessageID id, Args...args) 
        : tMessage(
            (MessageHeader *)new uint8_t[sizeof(T)+sizeof(*m_head)], // dynamic allocation
            id, sizeof(T))
    {
        new (m_head+1) T(std::forward<Args>(args)...);
    }

    // destructor
    ~aMessage ()
    {
        delete m_head;
    }

    // syntactic sugar to access contents
    T& contents(void) { return *(T*)(m_head+1); }
};

// -----------------------------------------
// specialization for small messages
// -----------------------------------------
template<typename T>
class aMessage<true, T> : public tMessage
{
    // message body defined locally
    MessageHeader m_header;
    uint8_t       m_data[sizeof(T)]; // no need for 64 bytes here

public:
    // in-place constructor
    template<class... Args>
    aMessage(MessageID id, Args...args) 
        : tMessage(
            &m_header, // local storage
            id, sizeof(T))
    {
        new (m_head+1) T(std::forward<Args>(args)...);
    }

    // syntactic sugar to access contents
    T& contents(void) { return *(T*)(m_head+1); }
};


// -----------------------------------------
// helper macro to hide template ugliness
// -----------------------------------------
#define Message(T) aMessage<storage<T>::local, T>
// something like typedef aMessage<storage<T>::local, T> Message<T>

// ==========================================================================
// Example
// ==========================================================================
#include <cstdio>
#include <cstring>

// message sending
int some_DMA_trick(int destination, void * data, uint16_t size)
{
    printf("sending %d bytes @%p to %08X\n", size, data, destination);
    return 1;
}

// some dynamic contents
struct gizmo {
    char * s;
    gizmo(void) { s = nullptr; };
    gizmo (const gizmo&  g) = delete;

    gizmo (const char * msg)
    {
        s = new char[strlen(msg) + 3];
        strcpy(s, msg);
        strcat(s, "#");
    }

    gizmo (gizmo&& g)
    {
        s = g.s;
        g.s = nullptr;
        strcat(s, "*");
    }

    ~gizmo() 
    { 
        delete s;
    }

    gizmo& operator=(gizmo g)
    {
        std::swap(s, g.s);
        return *this;
    }
    bool operator!=(gizmo& g)
    {
        return strcmp (s, g.s) != 0;
    }

};

// some small contents
struct small {
    int a, b, c;
    gizmo g;
    small (gizmo g, int a, int b, int c)
        : a(a), b(b), c(c), g(std::move(g)) 
    {
    }

    void trace(void) 
    { 
        printf("small: %d %d %d %s\n", a, b, c, g.s);
    }
};

// some big contents
struct big {
    gizmo lump[1000];

    big(const char * msg = "?")
    { 
        for (size_t i = 0; i != sizeof(lump) / sizeof(lump[0]); i++)
            lump[i] = gizmo (msg);
    }

    void trace(void)
    {
        printf("big: set to ");
        gizmo& first = lump[0];
        for (size_t i = 1; i != sizeof(lump) / sizeof(lump[0]); i++)
            if (lump[i] != first) { printf(" Erm... mostly "); break; }
        printf("%s\n", first.s);
    }
};

int main(void)
{
    // macros
    BuildMessage(mmsg1, small, WorldPeace, gizmo("Hi"), 1, 2, 3);
    BuildMessage(mmsg2, big  , Armaggedon, "Doom");
    ((small *)mmsg1.location())->trace();
    ((big   *)mmsg2.location())->trace();
    mmsg1.send(0x1000);
    mmsg2.send(0x2000);

    // templates
    Message (small) tmsg1(MessageID::WorldPeace, gizmo("Hello"), 4, 5, 6);
    Message (big  ) tmsg2(MessageID::Armaggedon, "Damnation");
    tmsg1.contents().trace();
    tmsg2.contents().trace();
    tmsg1.send(0x3000);
    tmsg2.send(0x4000);
}

:

small: 1 2 3 Hi#*
big: set to Doom#
sending 20 bytes @0xbf81be20 to 00001000
sending 4004 bytes @0x9e58018 to 00002000
small: 4 5 6 Hello#**
big: set to Damnation#
sending 20 bytes @0xbf81be0c to 00003000
sending 4004 bytes @0x9e5ce50 to 00004000

.

, , , , , .

, , . . .

, .

- , .

. , .

68 (64 - sizeof (contents object)).

, , . , .

, . , , .

, , send.
, IMHO.

, , . , BSP.

, , , . .

, - , , , .

, , , ( ) .

, 64 . , - , , .
, , , , , .

, , , .

-1

Source: https://habr.com/ru/post/1524798/


All Articles