如何从std: basic_ios中获得一个文件描述符,用于OS X上的clang ?

时间:2022-11-23 10:08:52

I'm porting some code to Darwin OS X and as part of the change, we go from gcc to the clang compiler.

我正在向Darwin OS X移植一些代码,作为更改的一部分,我们从gcc转到clang编译器。

In the code, there is a function dating from 2005 and posted several places on the Internet. It provides functionality for several different old versions of GCC, I've edited out all but the last it offers, v3.4.0 or later. The code depends upon two GCC specific classes: __gnu_cxx::stdio_filebuf and __gnu_cxx::stdio_sync_filebuf.

在代码中,有一个功能可以追溯到2005年,并在互联网上发布了几个位置。它为几个不同的旧版本的GCC提供了功能,除最后一个版本(v3.4.0或更高版本)外,我已经删除了所有功能。代码依赖于两个GCC特定的类:__gnu_cxx::stdio_filebuf和__gnu_cxx::stdio_sync_filebuf。

//! Similar to fileno(3), but taking a C++ stream as argument instead of a
//! FILE*.  Note that there is no way for the library to track what you do with
//! the descriptor, so be careful.
//! \return  The integer file descriptor associated with the stream, or -1 if
//!   that stream is invalid.  In the latter case, for the sake of keeping the
//!   code as similar to fileno(3), errno is set to EBADF.
//! \see  The <A HREF="http://www.ginac.de/~kreckel/fileno/">upstream page at
//!   http://www.ginac.de/~kreckel/fileno/</A> of this code provides more
//!   detailed information.
template <typename charT, typename traits>
inline int
fileno_hack(const std::basic_ios<charT, traits>& stream)
{
  // Some C++ runtime libraries shipped with ancient GCC, Sun Pro,
  // Sun WS/Forte 5/6, Compaq C++ supported non-standard file descriptor
  // access basic_filebuf<>::fd().  Alas, starting from GCC 3.1, the GNU C++
  // runtime removes all non-standard std::filebuf methods and provides an
  // extension template class __gnu_cxx::stdio_filebuf on all systems where
  // that appears to make sense (i.e. at least all Unix systems).  Starting
  // from GCC 3.4, there is an __gnu_cxx::stdio_sync_filebuf, in addition.
  // Sorry, darling, I must get brutal to fetch the darn file descriptor!
  // Please complain to your compiler/libstdc++ vendor...
#if defined(__GLIBCXX__) || defined(__GLIBCPP__)
  // OK, stop reading here, because it's getting obscene.  Cross fingers!
# if defined(__GLIBCXX__)  // >= GCC 3.4.0
  // This applies to cin, cout and cerr when not synced with stdio:
  typedef __gnu_cxx::stdio_filebuf<charT, traits> unix_filebuf_t;
  unix_filebuf_t* fbuf = dynamic_cast<unix_filebuf_t*>(stream.rdbuf());
  if (fbuf != NULL) {
    return fbuf->fd();
  }

  // This applies to filestreams:
  typedef std::basic_filebuf<charT, traits> filebuf_t;
  filebuf_t* bbuf = dynamic_cast<filebuf_t*>(stream.rdbuf());
  if (bbuf != NULL) {
    // This subclass is only there for accessing the FILE*.  Ouuwww, sucks!
    struct my_filebuf : public std::basic_filebuf<charT, traits> {
      int fd() { return this->_M_file.fd(); }
    };
    return static_cast<my_filebuf*>(bbuf)->fd();
  }

  // This applies to cin, cout and cerr when synced with stdio:
  typedef __gnu_cxx::stdio_sync_filebuf<charT, traits> sync_filebuf_t;
  sync_filebuf_t* sbuf = dynamic_cast<sync_filebuf_t*>(stream.rdbuf());
  if (sbuf != NULL) {
    return fileno(sbuf->file());
  }
# endif
#else
#  error "Does anybody know how to fetch the bloody file descriptor?"
  return stream.rdbuf()->fd();  // Maybe a good start?
#endif

  errno = EBADF;
  return -1;
}

The question is, for clang 5.1 on OS X Mavericks, what would be the way to computed the file descriptor for a std::basic_ios?

问题是,对于OS X Mavericks的clang 5.1,如何计算std的文件描述符::basic_ios?

1 个解决方案

#1


2  

If you don't mind getting your hands really dirty poking round in private implementation details the following code works:

如果您不介意在私有实现中弄脏您的手,下面的代码可以工作:

#include <iostream>
#include <fstream>

// Generate a static data member of type Tag::type in which to store
// the address of a private member. It is crucial that Tag does not
// depend on the /value/ of the the stored address in any way so that
// we can access it from ordinary code without directly touching
// private data.
template < class Tag >
struct stowed
{
  static typename Tag::type value;
};

template < class Tag >
typename Tag::type stowed< Tag >::value;

// Generate a static data member whose constructor initializes
// stowed< Tag >::value. This type will only be named in an explicit
// instantiation, where it is legal to pass the address of a private
// member.
template < class Tag, typename Tag::type x >
struct stow_private
{
  stow_private() { stowed< Tag >::value = x; }
  static stow_private instance;
};
template < class Tag, typename Tag::type x >
stow_private< Tag, x > stow_private< Tag, x >::instance;

struct filebuf_file { typedef FILE*( std::filebuf::*type ); };
template struct stow_private< filebuf_file, &std::filebuf::__file_ >;

FILE* c_file( std::filebuf& fb )
{
  return fb.*stowed< filebuf_file >::value;
}

int main(int argc, const char * argv[])
{
  std::ofstream fs("test.txt");
  FILE* file = c_file(*fs.rdbuf());
  std::cout << file->_file << "\n";
  return 0;
}

#1


2  

If you don't mind getting your hands really dirty poking round in private implementation details the following code works:

如果您不介意在私有实现中弄脏您的手,下面的代码可以工作:

#include <iostream>
#include <fstream>

// Generate a static data member of type Tag::type in which to store
// the address of a private member. It is crucial that Tag does not
// depend on the /value/ of the the stored address in any way so that
// we can access it from ordinary code without directly touching
// private data.
template < class Tag >
struct stowed
{
  static typename Tag::type value;
};

template < class Tag >
typename Tag::type stowed< Tag >::value;

// Generate a static data member whose constructor initializes
// stowed< Tag >::value. This type will only be named in an explicit
// instantiation, where it is legal to pass the address of a private
// member.
template < class Tag, typename Tag::type x >
struct stow_private
{
  stow_private() { stowed< Tag >::value = x; }
  static stow_private instance;
};
template < class Tag, typename Tag::type x >
stow_private< Tag, x > stow_private< Tag, x >::instance;

struct filebuf_file { typedef FILE*( std::filebuf::*type ); };
template struct stow_private< filebuf_file, &std::filebuf::__file_ >;

FILE* c_file( std::filebuf& fb )
{
  return fb.*stowed< filebuf_file >::value;
}

int main(int argc, const char * argv[])
{
  std::ofstream fs("test.txt");
  FILE* file = c_file(*fs.rdbuf());
  std::cout << file->_file << "\n";
  return 0;
}