繁体   English   中英

C++ 是否有像 Pascal 一样的“with”关键字?

[英]Does C++ have "with" keyword like Pascal?

Pascal 中with关键字可用于快速访问记录的字段。 有人知道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 this 看起来像这样

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”,这使得它变得更加困难。

您可以获得的最接近的是方法链接

myObj->setX(x)
     ->setY(y)
     ->setZ(z)

用于设置多个字段并using命名空间。

with (OBJECT) {CODE}

C++ 中没有这样的东西。
您可以将 CODE 原样放入 OBJECT 的方法中,但这并不总是可取的。

使用 C++11,您可以通过为 OBJECT 创建具有短名称的别名来非常接近。
例如,有问题的代码如下所示:

{
    auto &_ = *pointer;
    if (_.field1 && ... && _.fieldn) {...}
}

(周围的花括号用于限制别名_的可见性)

如果你经常使用某个字段,你可以直接给它取别名:

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->...
    ...
}

简化的未重载定义(选择一个):

it (Kotlin 风格):

#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循环总是只有一次传递,并且会被编译器优化掉。

在编写了许多解析器之后,这似乎是一个查找命名对象的死简单列表,无论是静态的还是动态的。 此外,我从未见过编译器无法正确识别丢失的对象和类型的情况,因此所有那些不允许 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;

以下方法依赖于 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 中的类内部发生的情况没有什么不同。 并且不要忽视它可以触发重要的编译器优化。

#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);
  }
}

这可能不会超过“与”提到的所有其他负面因素,但就像一个有趣的信息......

我也来自 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);
    }
}

也许你可以:

auto p = *pointer;
if (p.field1) && (p.field2) && ... (p.fieldn)

或者创建一个小程序,它可以理解 C++ with语句并将它们转换为某种形式的有效 C++。

一个简单的方法如下

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,只是在有人带我去清洁工之前把它报废了。 在一个更大的程序中,这是一个如何保持可靠计数的例子。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM