with
Pascal 中的关键字可用于快速访问记录的字段。有人知道C++是否有类似的东西吗?
例如:我有一个包含许多字段的指针,我不想这样输入:
if (pointer->field1) && (pointer->field2) && ... (pointer->fieldn)
我真正想要的是 C++ 中这样的东西:
with (pointer)
{
if (field1) && (field2) && .......(fieldn)
}
with
Pascal 中的关键字可用于快速访问记录的字段。有人知道C++是否有类似的东西吗?
例如:我有一个包含许多字段的指针,我不想这样输入:
if (pointer->field1) && (pointer->field2) && ... (pointer->fieldn)
我真正想要的是 C++ 中这样的东西:
with (pointer)
{
if (field1) && (field2) && .......(fieldn)
}
可能你能得到的最接近的是:(请不要对我投反对票;这只是一个学术练习。当然,你不能在这些人造with
块的主体中使用任何局部变量!)
struct Bar {
int field;
};
void foo( Bar &b ) {
struct withbar : Bar { void operator()() {
cerr << field << endl;
}}; static_cast<withbar&>(b)();
}
或者,更邪恶一点,
#define WITH(T) do { struct WITH : T { void operator()() {
#define ENDWITH(X) }}; static_cast<WITH&>((X))(); } while(0)
struct Bar {
int field;
};
void foo( Bar &b ) {
if ( 1+1 == 2 )
WITH( Bar )
cerr << field << endl;
ENDWITH( b );
}
或在 C++0x 中
#define WITH(X) do { auto P = &X; \
struct WITH : typename decay< decltype(X) >::type { void operator()() {
#define ENDWITH }}; static_cast<WITH&>((*P))(); } while(0)
WITH( b )
cerr << field << endl;
ENDWITH;
不,没有这样的关键字。
我喜欢使用:
#define BEGIN_WITH(x) { \
auto &_ = x;
#define END_WITH() }
例子:
BEGIN_WITH(MyStructABC)
_.a = 1;
_.b = 2;
_.c = 3;
END_WITH()
在 C++ 中,您可以将代码放在被引用的类的方法中pointer
。在那里,您可以直接引用成员而不使用指针。做到这一点inline
,你几乎得到你想要的。
尽管我主要在具有with
关键字的 Delphi 中编程(因为 Delphi 是 Pascal 派生词),但我不使用with
. 正如其他人所说:它节省了一点打字,但阅读变得更加困难。
在像下面的代码这样的情况下,可能很想使用with
:
cxGrid.DBTableView.ViewData.Records.FieldByName('foo').Value = 1;
cxGrid.DBTableView.ViewData.Records.FieldByName('bar').Value = 2;
cxGrid.DBTableView.ViewData.Records.FieldByName('baz').Value = 3;
使用with
这个看起来像这样
with cxGrid.DBTableView.ViewData.Records do
begin
FieldByName('foo').Value = 1;
FieldByName('bar').Value = 2;
FieldByName('baz').Value = 3;
end;
我更喜欢通过引入一个指向同一事物的额外变量来使用不同的技术with
。像这样:
var lRecords: TDataSet;
lRecords := cxGrid.DBTableView.ViewData.Records;
lRecords.FieldByName('foo').Value = 1;
lRecords.FieldByName('bar').Value = 2;
lRecords.FieldByName('baz').Value = 3;
这样就没有歧义,您可以节省一些打字时间,并且代码的意图比使用更清晰with
不,C++ 没有任何这样的关键字。
C++ 没有这样的特性。许多人认为 Pascal 中的“WITH”是一个问题,因为它会使代码模棱两可且难以阅读,例如很难知道 field1 是指针的成员还是局部变量或其他东西。Pascal 还允许使用多个带有变量,例如“With Var1,Var2”,这使得它变得更加困难。
with (OBJECT) {CODE}
C++ 中没有这样的东西。
您可以将 CODE 原样放入 OBJECT 的方法中,但这并不总是可取的。
使用 C++11,您可以通过为 OBJECT 创建具有短名称的别名来非常接近。
例如,有问题的代码如下所示:
{
auto &_ = *pointer;
if (_.field1 && ... && _.fieldn) {...}
}
(周围的花括号用于限制 alias 的可见性_
)
如果你经常使用某个字段,你可以直接给它取别名:
auto &field = pointer->field;
// Even shorter alias:
auto &_ = pointer->busy_field;
不,C/C++ 中没有with
关键字。
但是您可以使用一些预处理器代码添加它:
/* Copyright (C) 2018 Piotr Henryk Dabrowski, Creative Commons CC-BY 3.0 */
#define __M2(zero, a1, a2, macro, ...) macro
#define __with2(object, as) \
for (typeof(object) &as = (object), *__i = 0; __i < (void*)1; ++__i)
#define __with1(object) __with2(object, it)
#define with(...) \
__M2(0, ##__VA_ARGS__, __with2(__VA_ARGS__), __with1(__VA_ARGS__))
用法:
with (someVeryLongObjectNameOrGetterResultOrWhatever) {
if (it)
it->...
...
}
with (someVeryLongObjectNameOrGetterResultOrWhatever, myObject) {
if (myObject)
myObject->...
...
}
简化的未重载定义(选择一个):
未命名(Kotlin 风格it
):
#define with(object) \
for (typeof(object) &it = (object), *__i = 0; __i < (void*)1; ++__i)
命名:
#define with(object, as) \
for (typeof(object) &as = (object), *__i = 0; __i < (void*)1; ++__i)
当然,for
循环总是只有一次传递,并且会被编译器优化掉。
以下方法依赖于 Boost。如果您的编译器支持 C++0x auto
,那么您可以使用它并摆脱对 Boost 的依赖。
免责声明:请不要在任何必须由其他人(甚至在几个月内由您自己)维护或阅读的代码中执行此操作:
#define WITH(src_var) \
if(int cnt_ = 1) \
for(BOOST_AUTO(const & _, src_var); cnt_; --cnt_)
int main()
{
std::string str = "foo";
// Multiple statement block
WITH(str)
{
int i = _.length();
std::cout << i << "\n";
}
// Single statement block
WITH(str)
std::cout << _ << "\n";
// Nesting
WITH(str)
{
std::string another("bar");
WITH(another)
assert(_ == "bar");
}
}
首先我听说有人不喜欢'with'。规则非常简单,与 C++ 或 Java 中的类内部发生的情况没有什么不同。并且不要忽视它可以触发重要的编译器优化。
在编写了许多解析器之后,这似乎是一个查找命名对象的死简单列表,无论是静态的还是动态的。此外,我从未见过编译器无法正确识别丢失的对象和类型的情况,因此所有那些不允许 WITH ...ENDWITH 构造的蹩脚借口似乎都是胡扯。对于我们其他容易使用长对象名称的人来说,一种解决方法是创建简单的定义。无法抗拒,假设我有:
#include<something>
typedef int headache;
class grits{
public:
void corn(void);
void cattle(void);
void hay(void);}; //insert function defs here
void grits::grits(void)(printf("Welcome to Farm-o-mania 2012\n");};
#define m mylittlepiggy_from_under_the_backporch.
headache main(){
grits mylittlepiggy_from_under_the_backporch;
m corn(); //works in GCC
m cattle();
m hay();
return headache;
#include <iostream>
using namespace std;
template <typename T>
struct with_iter {
with_iter( T &val ) : p(&val) {}
inline T* begin() { return p; }
inline T* end() { return p+1; }
T *p;
};
#define with( N, I ) for( auto &N : with_iter<decltype(I)>(I) )
int main() {
with( out , cout ) {
out << "Hello world!" << endl;
}
return 0;
}
努夫说...
我可以看到“with”实际上有用的一个实例。
在递归数据结构的方法中,你经常会遇到这样的情况:
void A::method()
{
for (A* node = this; node; node = node->next) {
abc(node->value1);
def(value2); // -- oops should have been node->value2
xyz(node->value3);
}
}
像这样的错别字导致的错误很难找到。
用'with'你可以写
void A::method()
{
for (A* node = this; node; node = node->next) with (node) {
abc(value1);
def(value2);
xyz(value3);
}
}
这可能不会超过“与”提到的所有其他负面因素,但就像一个有趣的信息......
也许你可以:
auto p = *pointer;
if (p.field1) && (p.field2) && ... (p.fieldn)
或者创建一个小程序来理解with
C++ 中的语句并将它们转换为某种形式的有效 C++。
我也来自 Pascal 世界............而且我也喜欢 Python 的使用with
(基本上有一个自动尝试/最终):
with open(filename, "r") as file:
for line in file:
if line.startswith("something"):
do_more()
这就像一个智能 ptr 对象。打开失败不进块;离开块时,文件如果关闭。
这是一个非常接近 Pascal 的示例,同时也支持 Python 的使用(假设您有一个带有析构函数清理的智能对象);您需要更新的 C++ 标准编译器才能工作。
// Old way
cxGrid_s cxGrid{};
cxGrid.DBTableView.ViewData.Records.FieldByName.value["foo"] = 1;
cxGrid.DBTableView.ViewData.Records.FieldByName.value["bar"] = 2;
cxGrid.DBTableView.ViewData.Records.FieldByName.value["baz"] = 3;
// New Way - FieldByName will now be directly accessible.
// the `;true` is only needed if the call does not return bool or pointer type
if (auto FieldByName = cxGrid.DBTableView.ViewData.Records.FieldByName; true)
{
FieldByName.fn1 = 0;
FieldByName.fn2 = 3;
FieldByName.value["foo"] = 1;
FieldByName.value["bar"] = 2;
FieldByName.value["baz"] = 3;
}
如果你想要更接近:
#define with if
with (auto FieldByName = cxGrid.DBTableView.ViewData.Records.FieldByName; true)
// Similar to the Python example
with (smartFile sf("c:\\file.txt"); sf)
{
fwrite("...", 1, 3, *sf);
}
// Usage with a smart pointer
with (std::unique_ptr<class_name> p = std::make_unique<class_name>())
{
p->DoSomethingAmazing();
// p will be released and cleaned up upon exiting the scope
}
此示例的(快速而肮脏的)支持代码:
#include <map>
#include <string>
struct cxGrid_s {
int g1, g2;
struct DBTableView_s {
int tv1, tv2;
struct ViewData_s {
int vd1, vd2;
struct Records_s {
int r1, r2;
struct FieldByName_s{
int fn1, fn2;
std::map<std::string, int> value;
} FieldByName;
} Records;
} ViewData;
} DBTableView;
};
class smartFile
{
public:
FILE* f{nullptr};
smartFile() = delete;
smartFile(std::string fn) { f = fopen(fn.c_str(), "w"); }
~smartFile() { if (f) fclose(f); f = nullptr; }
FILE* operator*() { return f; }
FILE& operator->() { return *f; }
operator bool() const { return f != nullptr; }
};
我对 PotatoSwatter(目前已接受的答案)感到遗憾,我无法使用该解决方案访问在封闭范围内声明的变量。我试图在对 PotatoSwatter 的评论回复中发布此内容,但作为一个完整的帖子更好。这有点过头了,但是语法糖非常好!
#define WITH_SIG float x, float y, float z
#define WITH_ARG x, y, z
#define WITH(T,s) do { struct WITH : T { void operator()(s) {
#define ENDWITH(X,s) }}; static_cast<WITH&>((X))(s); } while(0)
class MyClass {
Vector memberVector;
static void myFunction(MyClass* self, WITH_SIG) {
WITH(MyClass, WITH_SIG)
memberVector = Vector(x,y,z);
ENDWITH(*self, WITH_ARG);
}
}
一个简单的方法如下
class MyClass
{
int& m_x;
public MyClass(int& x)
{
m_x = x;
m_x++;
}
~MyClass()
{
m_x--;
}
}
int main():
{
x = 0;
{
MyClass(x) // x == 1 whilst in this scope
}
}
我整天都在写 python,只是在有人带我去清洁工之前把它报废了。在一个更大的程序中,这是一个如何保持可靠计数的例子。