草庐IT

c++ - 如何用 C++ 编写 "meta if else if.."?

coder 2024-02-02 原文

我只是在学习 C++ 元编程的基础知识,我想看看其他人如何解决以下问题会很高兴。此外,很高兴看到使用 Boost 元编程库的解决方案,因为我认为它们是我的黑暗角落。所以问题是,可以更优雅地重写吗?

假设我们有以下结构:

template <std::size_t size>
struct type_factory
{
  typedef typename type_factory_impl<size>::type type;
};

这个结构应该是 typedef type,这取决于 size 的值。 type_factory_impltype_factory 的实现。用于确定类型的算法是:

if(size % bits<unsigned long long>::value == 0)
  typedef unsigned long long type;
else if(size % bits<unsigned long>::value == 0)
  typedef unsigned long type;
else if(size % bits<unsigned int>::value == 0)
  typedef unsigned int type;
else if(size % bits<unsigned short int>::value == 0)
  typedef unsigned short int type;
else if(size % bits<unsigned char>::value == 0)
  typedef unsigned char type;
else
  static_assert(false, "The type should be multiple of 'unsigned char' size");

我用两种方法解决了这个元程序。第一个是直接使用模式匹配,第二个是使用meta if-else。将以下内容视为两种解决方案之间的通用代码:

#include <cstddef>
#include <climits>

typedef unsigned char      uchar;
typedef unsigned short int usint;
typedef unsigned int       uint;
typedef unsigned long      ulong;
typedef unsigned long long ulonglong;

// Returns how many bits in Unsigned_Type
template <typename Unsigned_Type>
struct bits
{ enum { value = sizeof(Unsigned_Type)*CHAR_BIT }; };

// struct type_factory_impl ...

template <std::size_t size>
struct type_factory
{
  typedef typename type_factory_impl<size>::type type;
};

int main()
{
  auto a = type_factory<8>::type(0);  // unsigned char
  auto b = type_factory<16>::type(0); // unsigned short int
  auto c = type_factory<24>::type(0); // unsigned char
  auto d = type_factory<32>::type(0); // unsigned long
  auto e = type_factory<40>::type(0); // unsigned char
  auto f = type_factory<48>::type(0); // unsigned short int
  auto g = type_factory<56>::type(0); // unsigned char
  auto h = type_factory<64>::type(0); // unsigned long long
}

第一种方案:

template <bool is_uchar>
struct unsigned_char
{
  typedef unsigned char type;
  static_assert(is_uchar,
     "error: size must be multiple of 'unsigned char' size"); 
};
template <>
struct unsigned_char <true>
{ typedef uchar type; };

template <bool is_usint, std::size_t size>
struct unsigned_short_int
{ typedef typename
   unsigned_char<size % bits<uchar>::value == 0>::type type; };
template <std::size_t size>
struct unsigned_short_int <true, size>
{ typedef usint type; };

template <bool is_uint, std::size_t size>
struct unsigned_int
{ typedef typename
   unsigned_short_int<size % bits<usint>::value == 0, size>::type type; };
template <std::size_t size>
struct unsigned_int <true, size>
{ typedef uint type; };

template <bool is_ulong, std::size_t size>
struct unsigned_long
{ typedef typename
   unsigned_int<size % bits<uint>::value == 0, size>::type type; };
template <std::size_t size>
struct unsigned_long <true, size>
{ typedef ulong type; };

template <bool is_ulonglong, std::size_t size>
struct unsigned_long_long
{ typedef typename 
   unsigned_long<size % bits<ulong>::value == 0, size>::type type; };
template <std::size_t size>
struct unsigned_long_long <true, size>
{ typedef ulonglong type; };

template <std::size_t size>
struct type_factory_impl
{ typedef typename 
   unsigned_long_long<size % bits<ulonglong>::value == 0, size>::type type; };

第二种方案:

template <bool condition, typename Then, typename Else>
struct IF
{ typedef Else type; };
template <typename Then, typename Else>
struct IF <true, Then, Else>
{ typedef Then type; };

template <std::size_t size>
struct type_factory_impl
{
  typedef typename
    IF<size % bits<ulonglong>::value == 0, ulonglong,
      typename IF<size % bits<ulong>::value == 0, ulong,
        typename IF<size % bits<uint>::value == 0, uint,
          typename IF<size % bits<usint>::value == 0, usint,
            typename IF<size % bits<uchar>::value == 0, uchar, uchar>::type
          >::type
        >::type
      >::type
    >::type type;
};

最佳答案

和您一样,我认为 Boost.MPL 是黑魔法,所以我认为这可能是尝试使用它来回答您的问题的机会。请记住,这是我第一次尝试使用这个库,一些大师可能会提供更好的解决方案。

想法是使用boost::mpl::find_if查找类型序列中的第一个匹配项。

typedef boost::mpl::vector
    <
        unsigned long long,
        unsigned long,
        unsigned int,
        unsigned short,
        unsigned char
    > type_sequence;

template<std::size_t size>
struct predicate
{
    template<class T>
    struct apply {
        static const bool value = (size % bits<T>::value == 0);
    };
};

template<std::size_t size>
struct type_factory_impl
{
    typedef typename boost::mpl::find_if
        <
            type_sequence,
            typename predicate<size>::apply<boost::mpl::_1>
        >::type iterator_type;

    typedef typename boost::mpl::deref<iterator_type>::type type;
};

它似乎给了我很好的结果:

我不处理“默认”情况,但我的大脑刚刚开始流鼻血,我稍后会尝试完成我的回答,希望这会有所帮助。

关于c++ - 如何用 C++ 编写 "meta if else if.."?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4239632/

有关c++ - 如何用 C++ 编写 "meta if else if.."?的更多相关文章

  1. ruby-on-rails - rails : "missing partial" when calling 'render' in RSpec test - 2

    我正在尝试测试是否存在表单。我是Rails新手。我的new.html.erb_spec.rb文件的内容是:require'spec_helper'describe"messages/new.html.erb"doit"shouldrendertheform"dorender'/messages/new.html.erb'reponse.shouldhave_form_putting_to(@message)with_submit_buttonendendView本身,new.html.erb,有代码:当我运行rspec时,它失败了:1)messages/new.html.erbshou

  2. ruby-on-rails - 由于 "wkhtmltopdf",PDFKIT 显然无法正常工作 - 2

    我在从html页面生成PDF时遇到问题。我正在使用PDFkit。在安装它的过程中,我注意到我需要wkhtmltopdf。所以我也安装了它。我做了PDFkit的文档所说的一切......现在我在尝试加载PDF时遇到了这个错误。这里是错误:commandfailed:"/usr/local/bin/wkhtmltopdf""--margin-right""0.75in""--page-size""Letter""--margin-top""0.75in""--margin-bottom""0.75in""--encoding""UTF-8""--margin-left""0.75in""-

  3. ruby - 在 Ruby 中编写命令行实用程序 - 2

    我想用ruby​​编写一个小的命令行实用程序并将其作为gem分发。我知道安装后,Guard、Sass和Thor等某些gem可以从命令行自行运行。为了让gem像二进制文件一样可用,我需要在我的gemspec中指定什么。 最佳答案 Gem::Specification.newdo|s|...s.executable='name_of_executable'...endhttp://docs.rubygems.org/read/chapter/20 关于ruby-在Ruby中编写命令行实用程序

  4. ruby - 检查 "command"的输出应该包含 NilClass 的意外崩溃 - 2

    为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar

  5. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  6. ruby-on-rails - 迷你测试错误 : "NameError: uninitialized constant" - 2

    我遵循MichaelHartl的“RubyonRails教程:学习Web开发”,并创建了检查用户名和电子邮件长度有效性的测试(名称最多50个字符,电子邮件最多255个字符)。test/helpers/application_helper_test.rb的内容是:require'test_helper'classApplicationHelperTest在运行bundleexecraketest时,所有测试都通过了,但我看到以下消息在最后被标记为错误:ERROR["test_full_title_helper",ApplicationHelperTest,1.820016791]test

  7. ruby-on-rails - 相关表上的范围为 "WHERE ... LIKE" - 2

    我正在尝试从Postgresql表(table1)中获取数据,该表由另一个相关表(property)的字段(table2)过滤。在纯SQL中,我会这样编写查询:SELECT*FROMtable1JOINtable2USING(table2_id)WHEREtable2.propertyLIKE'query%'这工作正常:scope:my_scope,->(query){includes(:table2).where("table2.property":query)}但我真正需要的是使用LIKE运算符进行过滤,而不是严格相等。然而,这是行不通的:scope:my_scope,->(que

  8. 使用 ACL 调用 upload_file 时出现 Ruby S3 "Access Denied"错误 - 2

    我正在尝试编写一个将文件上传到AWS并公开该文件的Ruby脚本。我做了以下事情:s3=Aws::S3::Resource.new(credentials:Aws::Credentials.new(KEY,SECRET),region:'us-west-2')obj=s3.bucket('stg-db').object('key')obj.upload_file(filename)这似乎工作正常,除了该文件不是公开可用的,而且我无法获得它的公共(public)URL。但是当我登录到S3时,我可以正常查看我的文件。为了使其公开可用,我将最后一行更改为obj.upload_file(file

  9. ruby - 安装 Ruby 时遇到问题(无法下载资源 "readline--patch") - 2

    当我尝试安装Ruby时遇到此错误。我试过查看this和this但无济于事➜~brewinstallrubyWarning:YouareusingOSX10.12.Wedonotprovidesupportforthispre-releaseversion.Youmayencounterbuildfailuresorotherbreakages.Pleasecreatepull-requestsinsteadoffilingissues.==>Installingdependenciesforruby:readline,libyaml,makedepend==>Installingrub

  10. ruby - 用 Ruby 编写一个简单的网络服务器 - 2

    我想在Ruby中创建一个用于开发目的的极其简单的Web服务器(不,不想使用现成的解决方案)。代码如下:#!/usr/bin/rubyrequire'socket'server=TCPServer.new('127.0.0.1',8080)whileconnection=server.acceptheaders=[]length=0whileline=connection.getsheaders想法是从命令行运行这个脚本,提供另一个脚本,它将在其标准输入上获取请求,并在其标准输出上返回完整的响应。到目前为止一切顺利,但事实证明这真的很脆弱,因为它在第二个请求上中断并出现错误:/usr/b

随机推荐