2012-12-12 64 views
9

我在尝试实现自定义流类以在输出文件中生成很好的缩进代码时遇到了一些麻烦。我已经在网上进行了广泛的搜索,但似乎没有就实现这一目标达成共识。有些人在谈论推导流,别人说起推导出缓冲,但其他人建议使用的语言环境/面等C++自定义输出流与缩进

本质的,我发现自己写了很多这样的代码:

ofstream myFile(); 
myFile.open("test.php"); 
myFile << "<html>" << endl << 
      "\t<head>" << endl << 
      "\t\t<title>Hello world</title>" << endl << 
      "\t</head>" << endl << 
      "</html>" << endl; 

当标签开始增加它看起来可怕,它似乎将是不错的有这样的事情:

ind_ofstream myFile(); 
myFile.open("test.php"); 
myFile << "<html>" << ind_inc << ind_endl << 
      "<head>" << ind_inc << ind_endl << 
      "<title>Hello world</title>" << ind_dec << ind_endl << 
      "</head>" << ind_dec << ind_endl << 
      "</html>" << ind_endl; 

即创建一个派生流类,它会跟踪其当前缩进的深度,然后一些操纵器来增加/减少缩进深度和一个操作符来编写一个换行符,然后是很多标签。

因此,这里是我的投篮,在实现类&操纵:

ind_ofstream.h

class ind_ofstream : public ofstream 
{ 
    public: 
     ind_ofstream(); 
     void incInd(); 
     void decInd(); 
     size_t getInd(); 

    private: 
     size_t _ind; 
}; 

ind_ofstream& inc_ind(ind_ofstream& is); 
ind_ofstream& dec_ind(ind_ofstream& is); 
ind_ofstream& endl_ind(ind_ofstream& is); 

ind_ofstream.cpp

ind_ofstream::ind_ofstream() : ofstream() {_ind = 0;} 
void ind_ofstream::incInd()  {_ind++;} 
void ind_ofstream::decInd()  {if(_ind > 0) _ind--;} 
size_t ind_ofstream::getInd()  {return _ind;} 

ind_ofstream& inc_ind(ind_ofstream& is)  
{ 
    is.incInd(); 
    return is; 
} 

ind_ofstream& dec_ind(ind_ofstream& is)  
{ 
    is.decInd(); 
    return is; 
} 

ind_ofstream& endl_ind(ind_ofstream& is)  
{ 
    size_t i = is.getInd(); 
    is << endl; 
    while(i-- > 0) is << "\t"; 
    return is; 
} 

这建立,但不会产生预期的输出;任何使用自定义操纵器的尝试都会导致它们由于某种原因被转换为布尔值,并将“1”写入文件。我是否需要为我的新班级超载< <运营商? (我一直没能找到这样做的方法)

谢谢!

p.s.

1)我省略了#includes,使用我的代码片段中的命名空间等来节省空间。

2)我打算能够使用类似于我的第二个代码片段中的接口。如果在阅读完整篇文章后,您认为这是一个糟糕的主意,请解释原因并提供替代方案。

+1

问题:如果期望的结果是干净的代码和正确的输出 - 也就是说,如果这不仅仅是学术或为了自己的改善 - 为什么直接用这种方式写[HT | X] ML?至少,你可以把它写成未缩小的光盘,然后使用一些美化器(比如整洁)来做这种肮脏的工作。 ......据说,这很有趣,而且我有一种油腻的感觉,我很快就会在一起拼凑一个解决方案。 :) – Christopher

+0

嗨 - 这是一个好主意,如果我不能让计划A工作,最终可能会做什么。我发现流是C++更令人困惑的方面之一,所以我认为这可能是深入了解它们工作原理的好方法。 – user1898153

回答

8

iostreams支持向它们添加自定义数据,因此您不需要编写完整的派生类来添加将由操纵器操作的缩进级别。这是iostreams的一个鲜为人知的功能,但在这里派上用场。

你会写你的操纵这样的:

/* Helper function to get a storage index in a stream */ 
int get_indent_index() { 
    /* ios_base::xalloc allocates indices for custom-storage locations. These indices are valid for all streams */ 
    static int index = ios_base::xalloc(); 
    return index; 
} 

ios_base& inc_ind(ios_base& stream) { 
    /* The iword(index) function gives a reference to the index-th custom storage location as a integer */ 
    stream.iword(get_indent_index())++; 
    return stream; 
} 

ios_base& dec_ind(ios_base& stream) { 
    /* The iword(index) function gives a reference to the index-th custom storage location as a integer */ 
    stream.iword(get_indent_index())--; 
    return stream; 
} 

template<class charT, class traits> 
basic_ostream<charT, traits>& endl_ind(basic_ostream<charT, traits>& stream) { 
    int indent = stream.iword(get_indent_index()); 
    stream.put(stream.widen('\n'); 
    while (indent) { 
     stream.put(stream.widen('\t'); 
     indent--; 
    } 
    stream.flush(); 
    return stream; 
} 
0

我已经联合巴特货车不收Schenau的一个方面的解决方案,以允许推动和缩进层次的弹出现有的输出流。代码可以在GitHub:https://github.com/spacemoose/ostream_indenter,并有在仓库中进行更彻底的演示/测试,但基本上它可以让你做到以下几点:

/// This probably has to be called once for every program: 
// http://stackoverflow.com/questions/26387054/how-can-i-use-stdimbue-to-set-the-locale-for-stdwcout 
std::ios_base::sync_with_stdio(false); 

std::cout << "I want to push indentation levels:\n" << indent_manip::push 
      << "To arbitrary depths\n" << indent_manip::push 
      << "and pop them\n" << indent_manip::pop 
      << "back down\n" << indent_manip::pop 
      << "like this.\n" << indent_manip::pop; 

生产出:

I want to push indentation levels: 
    To arbitrary depths 
     and pop them 
    back down 
like this. 

我做一种讨厌的伎俩,所以我有兴趣听到关于代码实用程序的反馈。