Skip to content

Instantly share code, notes, and snippets.

@easyaspi314
Last active May 30, 2021 17:23
Show Gist options
  • Save easyaspi314/2eef19930e4e56f6aa492777943abfb0 to your computer and use it in GitHub Desktop.
Save easyaspi314/2eef19930e4e56f6aa492777943abfb0 to your computer and use it in GitHub Desktop.
C style void pointer/malloc wrapper for C++

C Style Malloc in C aka Void Pointers

One of the most annoying things about C++ is the incompatibility with C's void * conversion. In C, void * can be cast implicitly to any pointer type, but C++ needs an explicit cast.

Most C code is valid C++ code, but the most common issue is malloc, which returns void *.

For example, this is valid C code, but not valid C++ code:

struct Foo *x = malloc(sizeof(struct Foo));

C++ needs an explicit cast, which gets verbose.

struct Foo *x = (struct Foo *)malloc(sizeof(struct Foo));

This uses the VoidPtr class, which is typedefed to void * in C. Use it in place of it.

Include this header and enjoy.

#include "void_ptr.h"
struct Foo
{
int x;
int y;
};
int main(void)
{
/* No cast! */
struct Foo *foo = malloc(sizeof(struct Foo));
free(foo);
return 0;
}
/*
* Copyright (c) 2018 easyaspi314
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef VOID_PTR_H
#define VOID_PTR_H 1
#include <stdlib.h>
#ifdef __cplusplus
#include <utility>
class VoidPtr
{
void *ptr;
public:
/* use like malloc */
inline explicit VoidPtr(size_t count) : ptr(malloc(count)) {}
/* Wrapping a pointer */
template <typename T>
inline constexpr VoidPtr(T *data) : ptr(data) {}
inline constexpr VoidPtr(std::nullptr_t ) : ptr(nullptr) {}
/* The real magic. This lets it silently convert to any pointer you wish. */
template <typename T>
inline constexpr operator T *() const {
return reinterpret_cast<T *>(ptr);
}
/* Comparing to other pointers */
template <typename T>
inline constexpr bool operator==(T *other) const { return other == reinterpret_cast<T *>(ptr); }
template <typename T>
inline constexpr bool operator!=(T *other) const { return other != reinterpret_cast<T *>(ptr); }
/* basic comparisons */
inline constexpr bool operator==(std::nullptr_t) const { return ptr == nullptr; }
inline constexpr bool operator!=(std::nullptr_t) const { return ptr != nullptr; }
inline constexpr bool operator==(const VoidPtr &other) const { return ptr == other.ptr; }
inline constexpr bool operator!=(const VoidPtr &other) const { return ptr != other.ptr; }
inline bool operator==(size_t addr) const { return (size_t)ptr == addr; }
inline bool operator!=(size_t addr) const { return (size_t)ptr != addr; }
/* if (ptr) */
inline constexpr operator bool() const { return ptr != nullptr; }
inline VoidPtr &reallocate(size_t newlen) {
ptr = realloc(ptr, newlen);
return *this;
}
};
inline VoidPtr &&c_malloc(size_t count) {
return std::move(VoidPtr(count));
}
inline VoidPtr &&c_realloc(VoidPtr ptr, size_t newsize) {
return std::move(ptr.reallocate(newsize));
}
inline VoidPtr &&c_calloc(size_t nitems, size_t count) {
return std::move(VoidPtr(calloc(nitems, count)));
}
inline void c_free(VoidPtr ptr) {
if (ptr)
delete ptr;
}
/* new */
template <typename T>
inline T *c_allocate() {
return new T();
}
#define malloc c_malloc
#define calloc c_calloc
#define realloc c_realloc
#define free c_free
#define allocate c_allocate
#undef NULL
#define NULL nullptr
#else /* C code */
/*
* Not really a typedef, a typedef would make const VoidPtr
* void *const, which isn't really what you would want.
*/
#undef VoidPtr
#define VoidPtr void *
#define c_malloc malloc
#define c_calloc calloc
#define c_realloc realloc
#define c_free free
#define c_allocate(x) malloc(sizeof(x))
#define allocate c_allocate
#endif /* __cplusplus */
#endif /* VOID_PTR_H */
@aaangeletakis
Copy link

gcc 10 gives the error:

void_ptr.h:87:9: error: cannot delete expression of type 'VoidPtr'

@aaangeletakis
Copy link

aaangeletakis commented Jan 18, 2021

I have made modifcations to the above:

#ifndef VOID_PTR_H
#define VOID_PTR_H 1

#include <stdlib.h>
#include <utility>

class void_ptr
{
    void *ptr;
public:
    inline explicit void_ptr() : ptr() {}

    /* Wrapping a pointer */
    template <typename T>
    inline constexpr void_ptr(T *data) : ptr(data) {}

    inline constexpr void_ptr(std::nullptr_t ) : ptr(nullptr) {}

    /* The real magic. This lets it silently convert to any pointer you wish. */
    template <typename T>
    inline constexpr operator T *() const {
        return reinterpret_cast<T *>(ptr);
    }

    template <typename T>
    constexpr T* cast() const { return std::move((T *)ptr); }

    template <typename T>
    constexpr T get() const { return std::move(*(T *)ptr); }
    /* Comparing to other pointers */
    template <typename T>
    inline constexpr bool operator==(T *other) const { return other == reinterpret_cast<T *>(ptr); }
    template <typename T>
    inline constexpr bool operator!=(T *other) const { return other != reinterpret_cast<T *>(ptr); }

    /* basic comparisons */
    inline constexpr bool operator==(std::nullptr_t) const { return ptr == nullptr; }
    inline constexpr bool operator!=(std::nullptr_t) const { return ptr != nullptr; }
    inline constexpr bool operator==(const void_ptr &other) const { return ptr == other.ptr; }
    inline constexpr bool operator!=(const void_ptr &other) const { return ptr != other.ptr; }
    inline bool operator==(size_t addr) const { return (size_t)ptr == addr; }
    inline bool operator!=(size_t addr) const { return (size_t)ptr != addr; }

    /* if (ptr) */
    inline constexpr operator bool() const { return ptr != nullptr; }

    //so we can use regualar c++ void pointer syntax
    //int a = *(int*)ptr
    template <typename T>
    constexpr operator T() const {return std::move(cast<T>());}
};

#endif /* VOID_PTR_H */
#include <iostream>

#include "void_ptr.h"

int main(void)
{
    int foo = 8;
    void_ptr value = &foo;

    //C++ style
    int *   with_cast  = (int*)value;

    //C style
    int * without_cast = value;
    std::cout <<    *with_cast << '\n';
    std::cout << *without_cast << '\n';

    return 0;
}

@easyaspi314
Copy link
Author

This was meant as a joke. Please, don't use this in actual code. 😂

@aaangeletakis
Copy link

aaangeletakis commented Jan 19, 2021

Oh shit, I was actually fully prepared to use this in my state machine as it compiles the same as if it were a void * and it works wonderfully!
I know looking at your profile that a lot of the things you make are a joke, but this is actually useful.
Why is it bad to use this?

@aaangeletakis
Copy link

#ifndef VOID_PTR_T_H
#define VOID_PTR_T_H 1

#include <stdlib.h>
#include <utility>

/*
 * This void pointer class allows us to use C and C++ void pointer type casting
 * Ex.
 *      std::string str = "Hello World!";
 *      void_ptr_t value = &str;
 *      std::string * foo = value;
 *      std::cout << *foo << '\n';
 *
 *      OR
 *
 *      std::string boo = *(std::string *)value;
 *      std::cout << boo << '\n';
 *
 *      it compiles down to exactly the same as a (void *)
 */

class void_ptr_t
{
    void *ptr;
public:
    inline explicit void_ptr_t() : ptr() {}

    /* Wrapping a pointer */
    template <typename T>
    inline constexpr void_ptr_t(T *data) : ptr(data) {}

    inline constexpr void_ptr_t(std::nullptr_t ) : ptr(nullptr) {}

    /* The real magic. This lets it silently convert to any pointer you wish. */
    template <typename T>
    inline constexpr operator T *() const {
        return static_cast<T *>(ptr);
    }

    template <typename T>
    constexpr T cast() const { return (T)ptr; }

    template <typename T>
    constexpr T get() const { return *(T *)ptr; }
    /* Comparing to other pointers */
    template <typename T>
    inline constexpr bool operator==(T *other) const { return other == static_cast<T *>(ptr); }
    template <typename T>
    inline constexpr bool operator!=(T *other) const { return other != static_cast<T *>(ptr); }

    /* basic comparisons */
    inline constexpr bool operator==(std::nullptr_t) const { return ptr == nullptr; }
    inline constexpr bool operator!=(std::nullptr_t) const { return ptr != nullptr; }
    inline constexpr bool operator==(const void_ptr_t &other) const { return ptr == other.ptr; }
    inline constexpr bool operator!=(const void_ptr_t &other) const { return ptr != other.ptr; }
    inline bool operator==(size_t addr) const { return (size_t)ptr == addr; }
    inline bool operator!=(size_t addr) const { return (size_t)ptr != addr; }

    /* if (ptr) */
    inline constexpr operator bool() const { return ptr != nullptr; }

    //so we can use regualar c++ void pointer syntax
    //int a = *(int*)ptr
    template <typename T>
    constexpr operator T() const {return (T)ptr;}
};

#endif /* void_ptr_t_H */
#include <iostream>
#include <void_ptr.h>

int main(void)
{
    int foo = 8;
    void_ptr_t a = &foo;
  //int   b = a; //will not compile
    int * b = a; //will compile
  //std::cout << (int)a << '\n';   //will not compile
    std::cout << *(int*)a << '\n'; //will compile
    return 0;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment