自定义流操纵器,用于以任意基数流式传输整数

如何解决自定义流操纵器,用于以任意基数流式传输整数

| 我可以使一个“ 0”对象以十六进制输出整数
std::cout << std::hex << 0xabc; //prints `abc`,not the base-10 representation
是否有适用于所有基地的通用机械手?就像是
std::cout << std::base(4) << 20; //I want this to output 110
如果有一个,那么我没有其他问题了。 如果没有,那我可以写一个吗?是否不需要我访问“ 0”的私有实现详细信息? 请注意,我知道我可以编写一个接受数字并将其转换为字符串的函数,该字符串以任何基数表示该数字。或者,我可以使用已经存在的一种。我问的是自定义流操纵器-它们可能吗? 提前致谢     

解决方法

        您可以执行以下操作。我已对代码进行了注释,以解释每个部分的功能,但本质上是这样的: 创建一个\“ manipulator \”结构,使用
xalloc
iword
在流中存储一些数据。 创建一个自定义
num_put
构面,该构面将查找您的操纵器并应用操纵。 这是代码... 编辑:请注意,我不确定我在这里是否正确处理了
std::ios_base::internal
标志-因为我实际上不知道其用途。 编辑2:我找出ѭ7的含义,并更新了代码来处理。 编辑3:在
std::locacle::global
中添加了一个调用,以显示默认情况下如何使所有标准流类支持新的流操纵器,而不是必须
imbue
#include <algorithm>
#include <cassert>
#include <climits>
#include <iomanip>
#include <iostream>
#include <locale>

namespace StreamManip {

// Define a base manipulator type,its what the built in stream manipulators
// do when they take parameters,only they return an opaque type.
struct BaseManip
{
    int mBase;

    BaseManip(int base) : mBase(base)
    {
        assert(base >= 2);
        assert(base <= 36);
    }

    static int getIWord()
    {
        // call xalloc once to get an index at which we can store data for this
        // manipulator.
        static int iw = std::ios_base::xalloc();
        return iw;
    }

    void apply(std::ostream& os) const
    {
        // store the base value in the manipulator.
        os.iword(getIWord()) = mBase;
    }
};

// We need this so we can apply our custom stream manipulator to the stream.
std::ostream& operator<<(std::ostream& os,const BaseManip& bm)
{
    bm.apply(os);
    return os;
}

// convience function,so we can do std::cout << base(16) << 100;
BaseManip base(int b)
{
    return BaseManip(b);
}

// A custom number output facet.  These are used by the std::locale code in
// streams.  The num_put facet handles the output of numberic values as characters
// in the stream.  Here we create one that knows about our custom manipulator.
struct BaseNumPut : std::num_put<char>
{
    // These absVal functions are needed as std::abs doesnt support 
    // unsigned types,but the templated doPutHelper works on signed and
    // unsigned types.
    unsigned long int absVal(unsigned long int a) const
    {
        return a;
    }

    unsigned long long int absVal(unsigned long long int a) const
    {
        return a;
    }

    template <class NumType>
    NumType absVal(NumType a) const
    {
        return std::abs(a);
    }

    template <class NumType>
    iter_type doPutHelper(iter_type out,std::ios_base& str,char_type fill,NumType val) const
    {
        // Read the value stored in our xalloc location.
        const int base = str.iword(BaseManip::getIWord());

        // we only want this manipulator to affect the next numeric value,so
        // reset its value.
        str.iword(BaseManip::getIWord()) = 0;

        // normal number output,use the built in putter.
        if (base == 0 || base == 10)
        {
            return std::num_put<char>::do_put(out,str,fill,val);
        }

        // We want to conver the base,so do it and output.
        // Base conversion code lifted from Nawaz\'s answer

        int digits[CHAR_BIT * sizeof(NumType)];
        int i = 0;
        NumType tempVal = absVal(val);

        while (tempVal != 0)
        {
            digits[i++] = tempVal % base;
            tempVal /= base;
        }

        // Get the format flags.
        const std::ios_base::fmtflags flags = str.flags();

        // Add the padding if needs by (i.e. they have used std::setw).
        // Only applies if we are right aligned,or none specified.
        if (flags & std::ios_base::right || 
            !(flags & std::ios_base::internal || flags & std::ios_base::left))
        {
            std::fill_n(out,str.width() - i,fill);
        }

        if (val < 0)
        {
            *out++ = \'-\';
        }

        // Handle the internal adjustment flag.
        if (flags & std::ios_base::internal)
        {
            std::fill_n(out,fill);
        }

        char digitCharLc[] = \"0123456789abcdefghijklmnopqrstuvwxyz\";
        char digitCharUc[] = \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ\";

        const char *digitChar = (str.flags() & std::ios_base::uppercase)
            ? digitCharUc
            : digitCharLc;

        while (i)
        {
            // out is an iterator that accepts characters
            *out++ = digitChar[digits[--i]];
        }

        // Add the padding if needs by (i.e. they have used std::setw).
        // Only applies if we are left aligned.
        if (str.flags() & std::ios_base::left)
        {
            std::fill_n(out,fill);
        }

        // clear the width
        str.width(0);

        return out;
    }

    // Overrides for the virtual do_put member functions.

    iter_type do_put(iter_type out,long val) const
    {
        return doPutHelper(out,val);
    }

    iter_type do_put(iter_type out,unsigned long val) const
    {
        return doPutHelper(out,val);
    }
};

} // namespace StreamManip

int main()
{
    // Create a local the uses our custom num_put
    std::locale myLocale(std::locale(),new StreamManip::BaseNumPut());

    // Set our locacle to the global one used by default in all streams created 
    // from here on in.  Any streams created in this app will now support the
    // StreamManip::base modifier.
    std::locale::global(myLocale);

    // imbue std::cout,so it uses are custom local.
    std::cout.imbue(myLocale);
    std::cerr.imbue(myLocale);

    // Output some stuff.
    std::cout << std::setw(50) << StreamManip::base(2) << std::internal << -255 << std::endl;
    std::cout << StreamManip::base(4) << 255 << std::endl;
    std::cout << StreamManip::base(8) << 255 << std::endl;
    std::cout << StreamManip::base(10) << 255 << std::endl;
    std::cout << std::uppercase << StreamManip::base(16) << 255 << std::endl;

    return 0;
}
    ,        定制操纵器确实是可能的。例如,请参阅此问题。我对通用基础不熟悉。     ,        您确实有两个独立的问题。我想您正在询问的那个问题完全可以解决。不幸的是,另一个则不是这样。 在流中分配和使用一些空间来保持某些流状态是可以预见的问题。流具有几个成员(
xalloc
iword
pword
),这些成员使您可以在流中的数组中分配位置,并在其中读取/写入数据。这样,流操纵器本身是完全可能的。您基本上会使用ѭ4来在流的数组中分配一个点来保存当前基数,插入运算符在转换数字时将使用该点。 我看不到解决方案的问题相当简单:标准库已经提供了一个ѭ16来将ѭ17插入流中,并且它显然不知道您的假设数据为转换奠定了基础。您不能重载它,因为它需要与现有签名完全相同的签名,因此您的重载将是模棱两可的。
int
short
等的重载是成员函数的重载。我想,如果您想做的足够厉害,可以使用模板重载
operator<<
。如果我没记错的话,那将比库提供的非模板函数的精确匹配更好。您仍然会违反规则,但是如果将这样的模板放在命名空间std中,则至少有一定机会可以使用它。     ,        我试图编写代码,但它有一些限制。正如其他人(尤其是@Jerry)所指出的那样,它不是流操纵器,因此根本不可能。 这是我的代码:
struct base
{
   mutable std::ostream *_out;
   int _value;

   base(int value=10) : _value(value) {}

   template<typename T>
   const base& operator << (const T & data) const
   {
        *_out << data;
        return *this;
   }
   const base& operator << (const int & data) const
   {
        switch(_value)
        {
            case 2:  
            case 4:  
            case 8:  return print(data);
            case 16: *_out << std::hex << data; break;
            default:  *_out << data; 
        }
        return *this;
   }
   const base & print(int data) const
   {
        int digits[CHAR_BIT * sizeof(int)],i = 0;
        while(data)
        {
             digits[i++] = data % _value;  
             data /= _value;
        }
        while(i) *_out << digits[--i] ;
        return *this;
   }
   friend const base& operator <<(std::ostream& out,const base& b)   
   {
       b._out = &out;
       return b;
   }
};
这是测试代码:
int main() {
   std::cout << base(2) << 255 <<\",\" << 54 << \",\" << 20<< \"\\n\";
   std::cout << base(4) << 255 <<\",\" << 20<< \"\\n\";
   std::cout << base(8) << 255 <<\",\" << 20<< \"\\n\";
   std::cout << base(16) << 255 <<\",\" << 20<< \"\\n\";
}
输出:
11111111,110110,10100
3333,312,110
377,66,24
ff,36,14
在线演示:http://www.ideone.com/BWhW5 局限性: 基本不能更改两次。所以这将是一个错误:
std::cout << base(4) << 879 << base(8) << 9878 ; //error
使用ѭ25after后不能使用其他机械手:
std::cout << base(4) << 879 << std::hex << 9878 ; //error
std::cout << std::hex << 879 << base(8) << 9878 ; //ok
使用
base
后不能使用
std::endl
std::cout << base(4) << 879 << std::endl ; //error
//that is why I used \"\\n\" in the test code.
    ,        我不认为任意流都可以使用语法(使用操纵器,@ gigantt链接了显示一些替代性非操纵器解决方案的答案)。标准操纵器仅设置在流内部实现的选项。 OTOH,您当然可以使以下语法起作用:
std::cout << base(4,20);
其中“ 25”是提供流插入运算符的对象(无需返回临时“ 32”)。     

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-