如何在OS X上使用clang从std::basic_ios获取文件描述符?

9
我正在将一些代码移植到Darwin OS X,这意味着我们需要从gcc编译器转换为clang编译器。
代码中有一个函数,始于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;
}

问题是,在 OS X Mavericks 上的 clang 5.1 中,如何计算 std::basic_ios 的文件描述符呢?


1
我可以告诉你,使用的C++运行时很可能是LLVM项目的libc ++,它的basic_filebuf类似乎将一个stdio FILE包装起来,而不是直接使用操作系统级别的文件描述符。但是我没有找到任何从filebuf中提取FILE或从已经打开的FILE创建filebuf的方法。也许有比我更聪明的人知道怎么做。 - zwol
你解决了吗?你做了什么? - Paulo1205
@Paulo1205问题从未得到解决,我的调用代码只是处理return -1errno == EBADF的情况。 - WilliamKF
1个回答

6

如果您不介意深入了解私有实现细节,以下代码可以正常工作:

#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;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接