Bug #21498
Updated by cfis (Charlie Savage) about 16 hours ago
I am trying to wrap a simple C++ library, https://github.com/baderouaich/BitmapPlusPlus, as a Ruby extension. However when I use the extension to write a bitmap to disk the bitmap is corrupted. This is because the library uses std::ofstream which eventually uses the C API fclose to write the final bytes to the bitmap file and then closes it. The problem is that Ruby overrides `fclose` and replaces it with `rb_w32_fclose`. It then *exports* its custom version from from x64-vcruntime140-ruby340.dll. And the exported version is broken (at least from the standpoint of the C standard library). Note this has been a long standing issue. The first report I see is from 2013: https://bugs.ruby-lang.org/issues/8569 More recently in 2020 (which explains the issue very well): https://github.com/NREL/OpenStudio/issues/3942#issuecomment-610673401 I understand that Ruby is trying to provide a platform independent API. But the problem is this solution breaks any third party libraries that rely on these C APIs (which of course are very common). And there is no good workaround (see https://github.com/NREL/OpenStudio/issues/3942#issuecomment-611072774). So would it be possible for Ruby to stop exporting custom versions of basic C APIs? The code that does it is here: https://github.com/ruby/ruby/blob/master/win32/mkexports.rb#L41 Ruby of course could still use its custom versions of `fclose`, read, write etc within ruby.exe and x64-vcruntime140-ruby340.dll. But they should not be exported from x64-vcruntime140-ruby340.dll and thus be off limits to extensions. If a case comes up where an extension really needs access to rb_w32_fclose instead of fclose then an extension developer can use an `#ifdef _WIN32` to do so and work across platforms. That at least puts the developer in control versus now where I don't see any way I can wrap the bitmap library as a Ruby extension. From my experience the biggest problem is the replacing of fclose with rb_w32_fclose. This is the list of generated overrides: But I propose removing all such mappings, which include: FD_CLR=rb_w32_fdclr FD_ISSET=rb_w32_fdisset Sleep=rb_w32_Sleep accept=rb_w32_accept access=rb_w32_uaccess bind=rb_w32_bind close=rb_w32_close connect=rb_w32_connect dup2=rb_w32_dup2 fclose=rb_w32_fclose fstat=rb_w32_fstati128 get_osfhandle=rb_w32_get_osfhandle getcwd=rb_w32_ugetcwd getenv=rb_w32_ugetenv gethostbyaddr=rb_w32_gethostbyaddr gethostbyname=rb_w32_gethostbyname gethostname=rb_w32_gethostname getpeername=rb_w32_getpeername getpid=rb_w32_getpid getppid=rb_w32_getppid getprotobyname=rb_w32_getprotobyname getprotobynumber=rb_w32_getprotobynumber getservbyname=rb_w32_getservbyname getservbyport=rb_w32_getservbyport getsockname=rb_w32_getsockname getsockopt=rb_w32_getsockopt inet_ntop=rb_w32_inet_ntop inet_pton=rb_w32_inet_pton ioctlsocket=rb_w32_ioctlsocket isatty=rb_w32_isatty listen=rb_w32_listen lseek=rb_w32_lseek lstat=rb_w32_ulstati128 mkdir=rb_w32_umkdir mmap=rb_w32_mmap mprotect=rb_w32_mprotect munmap=rb_w32_munmap pipe=rb_w32_pipe pread=rb_w32_pread pwrite=rb_w32_pwrite read=rb_w32_read recv=rb_w32_recv recvfrom=rb_w32_recvfrom rename=rb_w32_urename rmdir=rb_w32_urmdir select=rb_w32_select send=rb_w32_send sendto=rb_w32_sendto setsockopt=rb_w32_setsockopt shutdown=rb_w32_shutdown socket=rb_w32_socket stati128=rb_w32_ustati128 strcasecmp=msvcrt.stricmp strerror=rb_w32_strerror strncasecmp=msvcrt.strnicmp times=rb_w32_times unlink=rb_w32_uunlink utime=rb_w32_uutime utimensat=rb_w32_uutimensat utimes=rb_w32_uutimes write=rb_w32_write