- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我最近遇到了很多情况,其中命名参数习语很有用,但我希望它在编译时得到保证。在链中返回引用的标准方法似乎总是调用运行时构造函数(使用 Clang 3.3 -O3 编译)。
我无法找到与此相关的任何内容,所以我试图让它与 constexpr
一起工作并获得一些功能:
class Foo
{
private:
int _a;
int _b;
public:
constexpr Foo()
: _a(0), _b(0)
{}
constexpr Foo(int a, int b)
: _a(a), _b(b)
{}
constexpr Foo(const Foo & other)
: _a(other._a), _b(other._b)
{}
constexpr Foo SetA(const int a) { return Foo(a, _b); }
constexpr Foo SetB(const int b) { return Foo(_a, b); }
};
...
Foo someInstance = Foo().SetB(5).SetA(2); //works
虽然这对于少量参数来说没问题,但对于较大的参数,它很快就会变得一团糟:
//Unlike Foo, Bar takes 4 parameters...
constexpr Bar SetA(const int a) { return Bar(a, _b, _c, _d); }
constexpr Bar SetB(const int b) { return Bar(_a, b, _c, _d); }
constexpr Bar SetC(const int c) { return Bar(_a, _b, c, _d); }
constexpr Bar SetD(const int d) { return Bar(_a, _b, _c, d); }
有没有更好的方法?我正在考虑使用具有许多(30 多个)参数的类来执行此操作,如果将来进行扩展,这似乎很容易出错。
编辑: 删除了 C++1y 标签——虽然 C++1y 似乎确实解决了问题(感谢 TemplateRex!)这是用于生产代码,我们坚持使用 C++ 11.如果这意味着它不可能,那么我想事情就是这样。
EDIT2:为了说明我为什么要找这个,这里有一个用例。目前在我们的平台上,开发人员需要为硬件配置显式设置位 vector ,虽然这没问题,但很容易出错。有些正在使用 C99 扩展中的指定初始化器,这没问题但不是标准的:
HardwareConfiguration hardwareConfig = {
.portA = HardwareConfiguration::Default,
.portB = 0x55,
...
};
然而,大多数人甚至没有使用它,而只是输入一团数字。因此,作为一项工作改进,我想朝着这样的方向发展(因为它也会强制使用更好的代码):
HardwareConfiguration hardwareConfig = HardwareConfiguration()
.SetPortA( Port().SetPolarity(Polarity::ActiveHigh) )
.SetPortB( Port().SetPolarity(Polarity::ActiveLow) );
这可能会更冗长,但在稍后阅读时会更清晰。
最佳答案
使用模板元编程
这是我为解决您的问题(至少部分解决)而想出的办法。通过使用模板元编程,您可以利用编译器为您完成大部分工作。对于那些以前从未见过此类代码的人来说,这些技术看起来很奇怪,但值得庆幸的是,大部分复杂性都可以隐藏在 header 中,用户只需以简洁明了的方式与库进行交互。
示例类定义及其使用
这里是一个例子,说明定义一个类需要你做什么:
template <
//Declare your fields here, with types and default values
typename PortNumber = field<int, 100>,
typename PortLetter = field<char, 'A'>
>
struct MyStruct : public const_obj<MyStruct, PortNumber, PortLetter> //Derive from const_obj like this, passing the name of your class + all field names as parameters
{
//Your setters have to be declared like this, by calling the Set<> template provided by the base class
//The compiler needs to be told that Set is part of MyStruct, probably because const_obj has not been instantiated at this point
//in the parsing so it doesn't know what members it has. The result is that you have to use the weird 'typename MyStruct::template Set<>' syntax
//You need to provide the 0-based index of the field that holds the corresponding value
template<int portNumber>
using SetPortNumber = typename MyStruct::template Set<0, portNumber>;
template<int portLetter>
using SetPortLetter = typename MyStruct::template Set<1, portLetter>;
template<int portNumber, char portLetter>
using SetPort = typename MyStruct::template Set<0, portNumber>
::MyStruct::template Set<1, portLetter>;
//You getters, if you want them, can be declared like this
constexpr int GetPortNumber() const
{
return MyStruct::template Get<0>();
}
constexpr char GetPortLetter() const
{
return MyStruct::template Get<1>();
}
};
使用类
int main()
{
//Compile-time generation of the type
constexpr auto myObject =
MyStruct<>
::SetPortNumber<150>
::SetPortLetter<'Z'>();
cout << myObject.GetPortNumber() << endl;
cout << myObject.GetPortLetter() << endl;
}
大部分工作由 const_obj
完成模板。它提供了一种在编译时修改对象的机制。很像 Tuple
,使用基于 0 的索引访问字段,但这不会阻止您使用友好名称包装 setter,就像上面的 SetPortNumber 和 SetPortLetter 所做的那样。 (他们只是转发到 Set<0> 和 Set<1>)
关于存储
在当前的实现中,在调用了所有的 setter 并声明了对象之后,字段最终存储在一个 const unsigned char
的紧凑数组中。名为 data
在基类中。如果您使用的字段不是无符号字符(例如上面使用 PortNumber 所做的那样),则该字段将分为 big endien unsigned char
的(可以根据需要更改为 little endien)。如果您不需要具有实际内存地址的实际存储,则可以通过修改 packed_storage
来完全省略它。 (请参阅下面的完整实现链接)并且这些值在编译时仍然可以访问。
限制
此实现仅允许整数类型用作字段(所有类型的短裤、整数、长整数、 bool 值、字符)。不过,您仍然可以提供作用于多个字段的 setter 。示例:
template<int portNumber, char portLetter>
using SetPort = typename MyStruct::template Set<0, portNumber>::
MyStruct::template Set<1, portLetter>;
完整代码
可以在此处找到实现这个小库的完整代码:
附加说明
此代码已经过测试,适用于 g++ 和 clang 的 C++11 实现。它已经好几个小时都没有经过测试,所以当然可能存在错误,但它应该为您提供一个良好的基础。希望对您有所帮助!
关于c++ - 带有 constexpr 的编译时命名参数习语,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19018642/
我面临以下问题: 我有一个命名空间 Exception\* , 其中包含多种类型 异常(exception)。 我有一个命名空间 Exception\User\* ,其中包含一个 特定类型的异常 (
新的 Highcharts v3.0 气泡图看起来很棒。是否可以用名称/一些文本注释和显示每个气泡? 谢谢,奈杰尔。 最佳答案 您需要做两件事。 首先,命名每个数据点(气泡): data: [ {
我通过使用 EVP_get_cipherbyname("AES-256-CTR") 获得了 EVP_CIPHER*,现在我想找到一种方法从 EVP_CIPHER* 返回到原始名称,在本例中为“AES-
为了避免 JavaScript 堆问题,我使用多个数组:family1、family2、family3 ...、dogs1、dogs2、dogs3 ... 使用示例:“family1 和 dogs1”
我很难理解这段代码。这不是我熟悉的典型 Javascript 函数语法。这是一个命名函数吗?或者这是更新事件的回调?抱歉,我对新手问题很陌生,我对 JS 还很陌生。我了解正在发生的一切,除了这个函数语
是否可以在 python 中执行以下操作? i=1 while True: w = open("POSCAR_i","w") i=i+1 if i<10:
我问这个是因为我刚刚在一段代码上看到它: var myVar = function func(arg){ console.log(arg); } 我不明白为什么函数在为 myVar 定义之前被“
我正在尝试为 ActiveDirectory 创建上下文(客户端和服务器都是 Windows),使用我的 Windows 凭据和 NTLM。 这是我的代码: public void func() {
我正在运行一个使用 JBoss5 容器的 ejb 示例。我正在使用一个例子 from here(Part one) . 在示例中,我在 JBoss 中部署了 bean,在 Tomcat 中部署了一个应
我希望能够命名一个 BackgroundWorker 以便于调试。这可能吗? 最佳答案 我必须尝试,但你不能只设置 Name BackgroundWorker 执行的 DoWork() 方法中的线程?
我在 Android Activity 和其他类之间遇到了越来越多的命名冲突。我想知道你能不能告诉我你是如何避免这些的。遗憾的是,关于 SO 的相关问题并未涵盖我的特定命名问题。 第一个例子 我有一个
当我尝试使用 loadChildren 加载模块以在命名 socket 中加载模块的组件时,出现抛出错误。 有没有办法在命名的路由器 socket 中延迟加载模块? //html //routing
很难说出这里问的是什么。这个问题是模棱两可的、模糊的、不完整的、过于宽泛的或修辞的,无法以目前的形式得到合理的回答。如需帮助澄清这个问题以便重新打开它,visit the help center .
在 Type Driven Development with Idris 第 6 章的代码中,我对这段代码感到困惑: data DataStore : Type -> Type where M
通常,如果有一个属性可以获取/设置状态值,我会使用“Is”,例如: Visibility: .IsVisible 但是对于获取/设置操作的属性,最好使用什么?喜欢: Casting shadows:
好的,所以如果你可以很容易地想到一个名词,那么命名一个接口(interface)(或类)很容易:用户、窗口、数据库、流等。 形容词或形容词的概念呢?例如有时间戳的东西(HasTimestamp、Tim
我刚开始学习 PowerShell,我想知道 Posh 中的 cmdlet(或高级功能,无论它们在 CTP3 中称为什么)是否有一些好的动词指南。 如果我做一个get-verb,我可以看到很多。但我仍
$(".song").live('click', function songClick() { //do stuff }); 你能像上面那样命名一个函数,然后稍后再调用它吗?我尝试过,但没有成
关闭。这个问题是opinion-based .它目前不接受答案。 想改进这个问题?更新问题,以便 editing this post 可以用事实和引用来回答它. 7年前关闭。 Improve this
我的 Spring 应用程序中有两组类 - DTO 和实体。 在阅读了 Bob 叔叔的 Clean Code 之后,我比以往任何时候都更喜欢正确命名事物。 我坐下来重构我的一个 Spring 项目,但
我是一名优秀的程序员,十分优秀!