Osmose

I make websites and chiptunes!

  • he/him

AKAs:
Lapsed Neurotypical
JavaScript's Strongest Warrior
Fake Podcast Host
Thotleader
Vertically Integrated Boyfriend
Your Fave's Mutual
Certified 5x Severals by the RIAA
Inconsistently Medicated
The Source That Views Back
Carnally Known
The Alternative


Homepage
osmose.ceo/

Osmose
@Osmose

EDIT: Found a workaround

Presumably unrelated bits removed:

typedef struct {
	void *ctx;
	void (*release)(void *ctx);
} KMIF_SOUND_DEVICE;

typedef struct {
	KMIF_SOUND_DEVICE kmif;
} SNGSOUND;

static void sndrelease(SNGSOUND *sndp)
{
	if (sndp->logtbl) sndp->logtbl->release(sndp->logtbl->ctx);
	XFREE(sndp);
}

KMIF_SOUND_DEVICE *SNGSoundAlloc(Uint32 sng_type)
{
	SNGSOUND *sndp;
	sndp = XMALLOC(sizeof(SNGSOUND));
	if (!sndp) return 0;
    sndp->kmif.ctx = sndp;
	sndp->kmif.release = sndrelease;
}

The error is: error: incompatible function pointer types assigning to 'void (*)(void *)' from 'void (SNGSOUND *)' [-Wincompatible-function-pointer-types] on the sndp->kmif.release = sndrelease; line.

I'm 80% sure this compiled fine on an earlier version of clang, and this got elevated from a warning to an error and I should probably just disable it, but I'm kinda curious why it's failing? My understanding of C++ is that the sndrelease variable should be a pointer to a function, and the SNGSOUND* pointer should be compatible with the void* pointer argument. So why does the compiler think they're incompatible?

(This is a vendored dependency so I can change the code to fix the bug but I'd rather disable the warning if the fix isn't fairly simple.)


Osmose
@Osmose

EDIT: Progress has been made

I lost the page that mentioned it but it turns out void pointers are compatible with struct pointers, except not in return values or function arguments. You can still cast between them though, so I updated the code to do so:

static void sndrelease(void *sndpv)
{
    SNGSOUND *sndp = (SNGSOUND*)sndpv;
	if (sndp->logtbl) sndp->logtbl->release(sndp->logtbl->ctx);
	XFREE(sndp);
}

That solved that problem. Now I can build an archive file, but when I try to then use emscripten to turn that archive file into WASM+JS, I get this:

$ emcc -lembind -o libmusicplayer_lib.js -Wl,--whole-archive libmusicplayer_lib.a -Wl,--no-whole-archive
wasm-ld: error: libmusicplayer_lib.a(adplugin_register.cpp.o): undefined symbol: vtable for musix::AdPlugin
wasm-ld: error: libmusicplayer_lib.a(aoplugin_register.cpp.o): undefined symbol: vtable for musix::AOPlugin

This is all using cmake files that worked for building a native NodeJS add-on that called this code, so I'm not really sure why it can't find the implementations for these plugin classes. I know that .a files are archive files and you can inspect what's inside them:

$ nm libmusicplayer_lib.a | grep adplugin
adplugin_register.cpp.o:
0000739f t _GLOBAL__sub_I_adplugin_register.cpp

The ADPlugin implementation should be in a file named ADPlugin.cpp that's included through a chain of linked dependencies via CMake. There's a libadplugin.a file being made that includes it:

$ nm musicplayer/plugins/adplugin/libadplugin.a | grep AdPlugin
AdPlugin.cpp.o:

...but it's not being included in the final libmusicplayer_lib.a archive file. Why isn't CMake linking all the various archive files for dependencies into the final output archive file? They're all being build as static dependencies I'm pretty sure given that's the default and none of them specify STATIC or SHARED explicitly.

(Also I tried running that emcc command with all the .a files for the missing plugins and it successfully finds the class implementations, but then fails on dependencies of those plugins not being available. I'd rather figure out why CMake isn't linking them and their dependencies if possible.)


You must log in to comment.

in reply to @Osmose's post:

That changes the error to error: incompatible function pointer types assigning to 'void (*)(void *)' from 'void (*)(SNGSOUND *)' [-Wincompatible-function-pointer-types] which implies that it's the void* and SNGSOUND * that are incompatible. Which is useful to know, thanks! But still a head scratcher.

§7.3.14 allows to cast noexcept function pointers to non-noexcept counterparts, §7.6.1.10.6 allows between any two function pointer types with reinterpret_cast but says that the result is Unspecified and that calling the result is Undefined Behaviour™, and §7.6.1.10.8 says that conversions from function pointers to object pointers (which exclude void*) are conditionally supported.

Embrace the UB and GNU C. You have nothing to lose but your chains.