C ++ - How to safely port malloc or emulate a new operator

Is there a generally accepted approach to wrapping malloc

in a function in C ++? What I'm trying to do is allocate arbitrary blocks of memory to store the output of a function that writes binary values ​​to a fixed-size buffer (ie: machine instructions). The approach I'm using now is:

class voidp {
  void *p;
public:
  voidp (void *pp) : p (pp) {}
  template<class T> operator T *() { return (T *) p; }
};

When converting C to C++, you can define malloc like this:

inline voidp
mymalloc (size_t size)
{
  return malloc (size);
}
#define malloc mymalloc

      

In many cases, it seems that explicit casts to (void*)

(at least in the case of my compiler) are invalid and I have to use the pattern-based approach above.

Is this approach safe and are there any implications for the "rule of three" (ie are there any constructors I need to disable or explicitly define / overload)?

Thank.

Links

Edit


The reason I am doing this is because I am trying to use something potentially better than just allocating a block of characters through void* buffer = new char[100]

. For further development, I am writing lower level code, which I recommend should be written in C ++ and not pure C. I should also have heap allocation methods that create chunks of memory on the heap that correspond to a 16 byte , 32-byte and 64-byte alignment as in the example below for 16-byte alignment.

{
  void *mem = malloc(1024+15);
  void *ptr = ((uintptr_t)mem+15) & ~ (uintptr_t)0x0F;
  memset_16aligned(ptr, 0, 1024);
  free(mem);
}

      

My application literally creates a bunch of low-level machine instructions in blocks that need to be 16/32/64 byte aligned (or this will cause a processor-level error) and then pass them on to the onboard DSP chip, which runs them as actual instructions rather than only input data.

+3


source to share


3 answers


Topic 1: Good old void*

?

C ++ has stronger typing than C. This is not a problem, but rather a solution! This way the compiler will catch a lot of nasty problems that will take you hours to know when to debug.

A simple example. The following code compiles to C. When I execute, I get a core dump.

FILE *fp;                     // file 
void *pbuff;                  // pointer to buffeer
pbuff = malloc(BUFSIZ); 
fp=fopen("test.txt", "rw");
fread(fp, BUFSIZ, 1, pbuff);  // ooops !!  Did you notice ? 

      

Why? I flipped fp and pbuff. Both are victims of void*

to anything*

and anything*

to constructs void*

. Of course, experienced programmers don't make mistakes in standard libraries! But what about the rest?

With C ++, this erroneous code does not compile as the conversion from void*

pbuff to is FILE*

recognized as a problem.

Topic 2: Is voidp pattern safe practice?

Well, C ++ has classes and inheritance. And a whole bunch of throws to highlight the weird pointer operations you could (and still allow) to do. Again C ++ makes it easier to find bugs. For example, reinterpret_cast<>

deserves more caution than a more controlledstatic_cast<>

To do things safe, you need to understand the C ++ object model and use the appropriate tools. Yours is voidp()

very smart, but it is not a suitable OOP tool.

Simple example, first my own way:

   struct A { std::string name; A(std::string s) : name(s) {} };
   struct B : A { int x, y;  B(std::string s, int a, int b) : A{ s }, x(a), y(b) {}  };
    ...
    A a{ "Chris" };
    B b{ "Tophe", 30, 40 };
    A *gpa = &a; 
    B *gpb = &b; 
    gpa = gpb;    // yes, works, because a B is an A and compiler knows it. 
    //gpb = gpa;  // No ! you can't without a recast !  

      

Now with your approach voidp

:

voidp pa(&a); 
voidp pb(&b);
pb = pa;  // no problem.  
gpb = pa;  // no problem ! Not even a cast to draw attention on potential issue !   

      



So you see, this is pretty insecure! He really hides nasty mistakes!

Topic 3: malloc()

better than new

?

Indeed, with help malloc()

you can easily create anything. And it's just as easy to create something the wrong size .... With help new

you can do weird things too, but it's harder to make basic mistakes.

new

can call the constructor of the object. malloc()

You will need additional steps with help .

Then when you have malloc()

, you have free()

. malloc()

/ are free()

great for the passive data structures you have in C. But in C ++, when you want to properly dispose of an object, you need to destroy it. Therefore, it is delete

really more appropriate.

Conclusion

I read your link with interest. It is true that it new

has limitations.

But I do not share the conclusions of the article. Instead of escaping new

and going back to malloc()

(again: it's perfect for C code, but just not suitable for C ++), this would be the best way to explore the standard library and use shared_ptr<>

other smart pointers , for example . They are much better at avoiding memory leaks than rewriting one own version of malloc ...

Edit

The specific usage you are doing can be done in C ++ as well

char buffer[1024 + 15];     // allocation  on stack, or char *mem = new char[1024+15]  
char *ptr = reinterpret_cast<char*>(((uintptr_t)&buffer + 15) & ~(uintptr_t)0x0F);   // Yes
std::fill_n(ptr, 0, 1024);     // is memset_16aligned() really portable ?  
// nothing if on stack         // or delete[] mem if new was used                 

      

There is also a function std::align()

that makes calculations for ptr. And there is placement-new for placing a C ++ object at a fixed address, like this:

char* p = new(ptr)char[1024];  // of course you shouldn't delete this one 

      

+2


source


If you are writing C ++ code. You have to use new and delete. Writing C style in C ++ is generally bad practice because the C ++ compiler will not generate optimal binaries. If you find yourself doing a lot of type casting with void *, you are probably wrong.



Conversely, use C ++ 11 smart pointers!

+1


source


new

and delete

are intended for low-level library development. There are STL containers available for allocating and de-allocating a buffer.

In your case, you can try

std::vector<unsigned char> myBuffer(MAX_SIZE);

      

0


source







All Articles