I'm writing some system code for a filesystem driver for an ESP32 based IoT gadget. My toolchain will always be gcc because the ESP32's developer framework requires GCC. The architecture is a known quantity. Portability is no concern. Function pointers are the same size as other function pointers and data pointers.
I've got a (googlable) struct called esp_vfs_t which somewhat simplified, looks like this
typedef struct
{
int flags;
union {
ssize_t (*write_p)(void* p, int fd, const void * data, size_t size);
ssize_t (*write)(int fd, const void * data, size_t size);
};
union {
off_t (*lseek_p)(void* p, int fd, off_t size, int mode);
off_t (*lseek)(int fd, off_t size, int mode);
};
union {
ssize_t (*read_p)(void* ctx, int fd, void * dst, size_t size);
ssize_t (*read)(int fd, void * dst, size_t size);
};
...
You can see that each function pointer is a union, but I'm only interested in the functions that take void* as their first argument.
Basically I want to OO this by creating a pure abstract class you can derive from, and because I want zero additional overhead for this feature, I want to copy the vtbl directly into this esp_vfs_t structure such that each function pointer is filled with the corresponding function pointer in my vtbl.
The code that I have to do it is ugly, but not crashing. It's simply not calling my functions for reasons I haven't determined yet.
I'm trying to assert that my function pointers got copied where I expect, but C++ won't let me compare addresses i need to compare in order to make it work. This is because the first argument for each function in the structure is void*, while of course the first argument to my vtbl functions are the class instance ptr itself.
The following code compiles and runs "successfully" if i remove the code to attempt to do that assert, but for whatever reason my functions aren't getting called (could be an unrelated issue)
What I expect is a crash or my functions to be called.
Ideally what I'd like for now is to get the code below that asserts to compile:
static bool mount(const char* mount_point,vfs_driver* driver) {
if(nullptr==mount_point || nullptr==driver) {
m_last_error=ESP_ERR_INVALID_ARG;
return false;
}
esp_vfs_t vfs;
memset(&vfs,0,sizeof(vfs));
vfs.flags = ESP_VFS_FLAG_CONTEXT_PTR;
void* test1;
void* test2;
memcpy(&test1,vfs.fstat,sizeof(void*));
memcpy(&test2,&esp32::vfs_driver::fstat,sizeof(void*));
memcpy(&vfs.write_p,driver,sizeof(vfs_driver));
assert(test1==test2);
esp_err_t res = esp_vfs_register(mount_point,&vfs,driver);
if(ESP_OK!=res) {
m_last_error = res;
return false;
}
return true;
}
Here's the first bit of the abstract base class I've created
class vfs_driver {
public:
virtual ssize_t write(int fd, const void * data, size_t size)=0;
virtual off_t lseek(int fd, off_t size, int mode)=0;
virtual ssize_t read(int fd, void * dst, size_t size)=0;
virtual ssize_t pread(int fd, void *dst, size_t size, off_t offset)=0;
virtual ssize_t pwrite(int fd, const void *src, size_t size, off_t offset)=0;
virtual int open(const char * path, int flags, int mode)=0;
virtual int close(int fd)=0;
virtual int fstat(int fd, struct stat * st)=0;
virtual int fsync(int fd)=0;
...
What I have tried:
The code I've tried is presented as part of the question