创建一个粘性操纵器,在输出流的每个标记之间插入分隔符

发布于 2025-01-18 05:35:59 字数 2849 浏览 3 评论 0原文

我已经看到了示例,可以使您创建一个插入定界器的操纵器,但这些操纵器都不粘。也就是说,操纵器返回一个特殊的类,该类别插入定界符,而不是永久修改输出流,以便它可以自己做。

我希望能够做到这一点:

std::cout << sep(", ");
std::cout << "hello" << "world";
// "hello, world"

此刻此打印“ h,e,e,l,l,o,w,o,r,l,l,l,d”当我需要它为“你好,世界” 。我获得错误的输出的原因是因为我将打印量放在溢出()方法和Overflow()中。我不确定将其放在哪里。

对不起,它是冗长的。如果我知道一种更简单的写作方法,我会的。只需从底部开始,然后从底部开始工作:

#include <iostream>
#include <cstring>

// index for delimiter
int separator() {
  static int idx = std::ios_base::xalloc();
  return idx;
}

// index for storage of dynamically-allocated buffer
int rdbuffer() {
  static int idx = std::ios_base::xalloc();
  return idx;
}

struct custom_separator : std::streambuf {
 public:
  custom_separator(std::ostream& _stream)
      : stream(_stream), buffer(_stream.rdbuf()) {}

  int_type overflow(int_type c) {
    // has a token been read already
    if (token_read) {
      char* delim = static_cast<char*>(stream.pword(separator()));
      // print delim
      buffer->sputn(delim, strlen(delim));
    }
    token_read = true;
    return buffer->sputc(c);
  }

 private:
  std::ostream& stream;
  std::streambuf* buffer;
  bool token_read = false;
};

// deletes the buffer and the delimiter
void cleanup(std::ios_base::event evt, std::ios_base& str, int idx) {
  if (evt == std::ios_base::erase_event) {
    delete static_cast<const char*>(str.pword(idx));
    delete static_cast<custom_separator*>(str.pword(rdbuffer()));
  }
}

std::ostream& set_separator(std::ostream& os, const char* str) {
  if (!os.bad()) {
    // If a separator string doesn't exist, assign os a buffer that prints one
    if (!os.pword(separator())) {
      auto buf = new custom_separator(os);
      os.rdbuf(buf);
      // this is to keep track of buf so we can delete it later
      os.pword(rdbuffer()) = static_cast<void*>(buf);
      os.register_callback(cleanup, separator());
    }
    // delete the previous separator
    delete static_cast<const char*>(os.pword(separator()));
    // store the new one
    os.pword(separator()) = (void*)(str);
  }
  return os;
}

struct sep {
  explicit sep(const char* _sep) 
    : separator(new char[strlen(_sep) + 1]) {
    strcpy(separator, _sep);
  }
  sep(const sep&) = delete;
  sep(const sep&&) = delete;
  char* separator;
};

std::ostream& operator<<(std::ostream& os, const sep& manip) {
  set_separator(os, manip.separator);
  return os;
}

int main() {
  std::cout << sep(", ");
  std::cout << "hello";
  std::cout << "world";
  // "h, e, l, l, o, w, o, r, l, d"
}

Overflow()的主要问题是,我不知道什么时候可以检测到令牌的结尾,例如“ Hello” 已被阅读以了解何时插入。

I've seen examples that allow you to create a manipulator that inserts delimiters but none of those manipulators are sticky. That is, the manipulator returns a special class that inserts the delimiter, rather than modifying the output stream permanently so that it can do it on its own.

I want to be able to do this:

std::cout << sep(", ");
std::cout << "hello" << "world";
// "hello, world"

At the moment this prints "h, e, l, l, o, w, o, r, l, d" when I need it to be "hello, world". The reason I'm getting the wrong output is because I put the printing in the overflow() method and overflow() is being called for each character. I'm not sure where is the appropriate place to put it.

Sorry about it being verbose. If I knew a simpler way to write it I would. Just start from the bottom and work your way up:

#include <iostream>
#include <cstring>

// index for delimiter
int separator() {
  static int idx = std::ios_base::xalloc();
  return idx;
}

// index for storage of dynamically-allocated buffer
int rdbuffer() {
  static int idx = std::ios_base::xalloc();
  return idx;
}

struct custom_separator : std::streambuf {
 public:
  custom_separator(std::ostream& _stream)
      : stream(_stream), buffer(_stream.rdbuf()) {}

  int_type overflow(int_type c) {
    // has a token been read already
    if (token_read) {
      char* delim = static_cast<char*>(stream.pword(separator()));
      // print delim
      buffer->sputn(delim, strlen(delim));
    }
    token_read = true;
    return buffer->sputc(c);
  }

 private:
  std::ostream& stream;
  std::streambuf* buffer;
  bool token_read = false;
};

// deletes the buffer and the delimiter
void cleanup(std::ios_base::event evt, std::ios_base& str, int idx) {
  if (evt == std::ios_base::erase_event) {
    delete static_cast<const char*>(str.pword(idx));
    delete static_cast<custom_separator*>(str.pword(rdbuffer()));
  }
}

std::ostream& set_separator(std::ostream& os, const char* str) {
  if (!os.bad()) {
    // If a separator string doesn't exist, assign os a buffer that prints one
    if (!os.pword(separator())) {
      auto buf = new custom_separator(os);
      os.rdbuf(buf);
      // this is to keep track of buf so we can delete it later
      os.pword(rdbuffer()) = static_cast<void*>(buf);
      os.register_callback(cleanup, separator());
    }
    // delete the previous separator
    delete static_cast<const char*>(os.pword(separator()));
    // store the new one
    os.pword(separator()) = (void*)(str);
  }
  return os;
}

struct sep {
  explicit sep(const char* _sep) 
    : separator(new char[strlen(_sep) + 1]) {
    strcpy(separator, _sep);
  }
  sep(const sep&) = delete;
  sep(const sep&&) = delete;
  char* separator;
};

std::ostream& operator<<(std::ostream& os, const sep& manip) {
  set_separator(os, manip.separator);
  return os;
}

int main() {
  std::cout << sep(", ");
  std::cout << "hello";
  std::cout << "world";
  // "h, e, l, l, o, w, o, r, l, d"
}

The main issue with overflow() is that I don't know when to detect when the end of a token like "hello" has been read to know when to insert.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

寻找一个思念的角度 2025-01-25 05:35:59

尝试以下操作。此外,还添加了新换行符处理(新行符号),以便在传输过程中(新行之后)不会添加分隔符。

#include <iostream>

class MyStream
{
    public:
        struct Sep
        {
            Sep (const std::string& sep_value = ""):
                m_sep(sep_value)
            {
            }
            
            operator std::string()
            {
                return m_sep;
            }
                
            private:
            std::string m_sep;
        };
        
        MyStream& operator << (const Sep& sep)
        {
            m_sep = sep;
            m_add_sep = false;
            return *this;
        }
        
        MyStream& operator << (const std::string& str)
        {
            if(str.find(MyStream::endl) != std::string::npos)
                m_add_sep = false;
            operator<< <std::string>(str);
            m_add_sep = false;
            return *this;
        }
        
        template <typename T>
        MyStream& operator << (const T& value)
        {
            if(m_add_sep)
                std::cout << static_cast<std::string>(m_sep);
            std::cout << value;
            m_add_sep = true;
            return *this;
        }
        
    static const std::string endl;
        
    private:
        Sep m_sep;
        bool m_add_sep = false;
};

const std::string MyStream::endl = std::string("\n");

int main()
{
    MyStream stream;
    stream << "hello" << "world" << MyStream::endl; // prints "helloworld"
    stream << MyStream::Sep(", ");
    stream << "hello" << "world" << MyStream::endl; // prints "hello, world"
    stream << 1 << 2;
    stream << 3 << MyStream::endl; // both lines prints "1, 2, 3"
    stream << MyStream::Sep();
    stream << 1 << 2 << 3 << MyStream::endl; // prints "123"
    return 0;
}

Try the following. Additionally, new line break processing (new line symbol) has been added so that the separator is not added during the transfer (after new line).

#include <iostream>

class MyStream
{
    public:
        struct Sep
        {
            Sep (const std::string& sep_value = ""):
                m_sep(sep_value)
            {
            }
            
            operator std::string()
            {
                return m_sep;
            }
                
            private:
            std::string m_sep;
        };
        
        MyStream& operator << (const Sep& sep)
        {
            m_sep = sep;
            m_add_sep = false;
            return *this;
        }
        
        MyStream& operator << (const std::string& str)
        {
            if(str.find(MyStream::endl) != std::string::npos)
                m_add_sep = false;
            operator<< <std::string>(str);
            m_add_sep = false;
            return *this;
        }
        
        template <typename T>
        MyStream& operator << (const T& value)
        {
            if(m_add_sep)
                std::cout << static_cast<std::string>(m_sep);
            std::cout << value;
            m_add_sep = true;
            return *this;
        }
        
    static const std::string endl;
        
    private:
        Sep m_sep;
        bool m_add_sep = false;
};

const std::string MyStream::endl = std::string("\n");

int main()
{
    MyStream stream;
    stream << "hello" << "world" << MyStream::endl; // prints "helloworld"
    stream << MyStream::Sep(", ");
    stream << "hello" << "world" << MyStream::endl; // prints "hello, world"
    stream << 1 << 2;
    stream << 3 << MyStream::endl; // both lines prints "1, 2, 3"
    stream << MyStream::Sep();
    stream << 1 << 2 << 3 << MyStream::endl; // prints "123"
    return 0;
}
眼前雾蒙蒙 2025-01-25 05:35:59
  1. 这是一个可能的解决方案。分离器是所有流的

     名称空间alt_std
     {
       结构Sep
       {
         sep(const char* s){s_ = s; }
    
         朋友STD :: Ostream&amp;操作员&lt;&lt;(std :: ostream&amp; os,const sep&amp; sm)
         {
           返回操作系统;
         }
    
         内联静态std :: string s_ {};
       };
    
       STD :: Ostream&amp;操作员&lt;&lt;(std :: ostream&amp; os,const char* s)
       {
         返回std ::操作员&lt;&lt;(OS,s),std ::操作员&lt;&lt;(OS,sep :: s_);
       }
     }
    
     int main()
     {
       使用名称空间alt_std;
    
       std :: cout&lt;&lt; sep(“”)&lt;&lt; “你好”;
       std :: cout&lt;&lt; “世界”&lt;&lt; std :: endl; //“ Hello World \ n”
    
       std :: cout&lt;&lt; sep(“”)&lt;&lt; “ Hel”&lt;&lt; “ LO”; // “你好”
     }
     
  2. 如果您想在流对象级别上进行操作,则更困难,因为流对象没有自定义存储空间您可以在其中存储分离器的值。

  3. ,或者您可以简单地包装流对象:

     模板&lt;键入OS&gt;
     结构包装器
     {
       wrapper(OS&amp; os,const char* sep):os_ {os},sep_ {sep} {} {}
    
       模板&lt; T&gt; typename
       朋友包装&amp;操作员&lt;&lt;(包装器&amp; os,const t&amp; v)
       {
         os.os_&lt;&lt; v&lt;&lt; os.sep_;
         返回操作系统;
       }
    
       OS&amp; OS_;
       std :: string sep_;
     };
    
     int main()
     {
       包装器OS {std :: Cout,“”};
       OS&lt;&lt; “你好”&lt;&lt; “世界”;
     }
     
  1. Here's a possible solution. The separator is the same for all streams.

     namespace alt_std
     {
       struct sep
       {
         sep(const char* s) { s_ = s; }
    
         friend std::ostream& operator<<(std::ostream& os, const sep& sm)
         {
           return os;
         }
    
         inline static std::string s_{};
       };
    
       std::ostream& operator<<(std::ostream& os, const char* s)
       {
         return std::operator<<(os, s), std::operator<<(os, sep::s_);
       }
     }
    
     int main()
     {
       using namespace alt_std;
    
       std::cout << sep(" ") << "hello";
       std::cout << "world" << std::endl; // "hello world\n"
    
       std::cout << sep("") << "hel" << "lo"; // "hello"
     }
    
  2. If you want to do it at the stream object level, it's more difficult because stream objects don't have a custom storage space where you can store the value of the separator.

  3. Or you could simply wrap the stream object:

     template< typename OS >
     struct wrapper
     {
       wrapper(OS& os, const char* sep) : os_{ os }, sep_{ sep } {}
    
       template< typename T>
       friend wrapper& operator<<(wrapper& os, const T& v)
       {
         os.os_ << v << os.sep_;
         return os;
       }
    
       OS& os_;
       std::string sep_;
     };
    
     int main()
     {
       wrapper os{ std::cout, " " };
       os << "hello" << "world";
     }
    
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文