NetBuffer - buffer specialized for socket read / write
One of the benefits of circular buffers is that they can be filled up to the entire size of the buffer, with completed fields being extracted from the head of the buffer immediately freeing up space.  This isn't the case for your buffer.  Space in the buffer is never freed until the entire buffer has been read, so if partial fields are consistently read there is a risk the buffer will get to a full state.

dataTail

You've declared your other access methods, including data as const.  Is it a mistake to not declare dataTail as const?  It's not entirely clear to me why a client would need to have a pointer to the bit of the buffer you haven't used.  Or are you expecting the client to actually write to the dataTail, without amending the tail_ afterwards?!?

push

Your extended push seems like it has a bug (or allows a very strange use case):

bool push(size_t const len, const void *ptr = nullptr){
    if (len == 0)
        return false;

    if (capacity() &lt; len)
        return false;

    if (ptr)
        memmove(&amp;buffer_[tail_], ptr, len);

    tail_ = (size_type) (tail_ + len);

    return true;
}


If ptr is null, then no bytes are added to the buffer, however the tail is still incremented as if data had been written.  So effectively, whatever is already in the buffer is added instead.  Having ptr default to null suggests this might be expected behaviour but it seems very odd.

print

print may be a debug method, however its output is confusing.  As you pop from the buffer, you're reducing the number of characters from the buffer that are displayed however you're still printing from the start of the buffer.  This gives the impression that pop is actually removing from the tail, rather than the head.  You should probably be printing from the head instead:

printf("h: %3u | t: %3u | ad: %3zu | ac: %3zu | %.*s\n",
        head_, tail_,
        size(), capacity(),
        (int) size(), data());