Project

General

Profile

Bug #21498

Updated by cfis (Charlie Savage) 1 day 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 to wrap the bitmap library for usage in Ruby. 

 From my experience the biggest problem is the replacing of fclose with rb_w32_fclose. 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 
 sendmsg 
 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

Back