【问题标题】:Does C++ have "with" keyword like Pascal?C++ 是否有像 Pascal 一样的“with”关键字?
【发布时间】:2010-02-17 08:19:43
【问题描述】:

Pascal 中的with 关键字可用于快速访问记录的字段。 有人知道 C++ 是否有类似的东西吗?

例如: 我有一个包含许多字段的指针,我不想这样输入:

if (pointer->field1) && (pointer->field2) && ... (pointer->fieldn)

我真正想要的是 C++ 中这样的东西:

with (pointer)
{
  if (field1) && (field2) && .......(fieldn)
}

【问题讨论】:

  • 嗯。 Javascript 有 with 关键字,它的作用基本相同;我没有意识到它的血统可以追溯到帕斯卡(!)。
  • 可能来自 Cobol 或 ADA,谁知道呢……
  • Ada 中有一个 with,但没有那个意思。
  • Vb和Vb.Net中也有一个with,意思相同。
  • 但是在VB中你需要说With obj / .X = x,所以它不是那么模棱两可

标签: c++ pascal with-statement


【解决方案1】:

你能得到的最接近的可能是这样的:(请不要对我投反对票;这只是一个学术练习。当然,你不能在这些人工 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;

【讨论】:

  • 非常好:)。我想提一下,对于我们这些使用 Metrowerks 编译器的少数人来说,这很可能不起作用(它不能很好地处理函数中的结构)
  • @arke:真的吗?令人惊讶的是,当它是 Mac 标准时,我喜欢 Metrowerks。嗯,看起来他们失去了霍华德·欣南特……
  • 你可以使用静态变量。
  • 在c++17中,最好是if(auto a=foo();true){/**/}
【解决方案2】:

不,没有这样的关键字。

【讨论】:

    【解决方案3】:

    我喜欢用:

        #define BEGIN_WITH(x) { \
            auto &_ = x;
    
        #define END_WITH() }
    

    例子:

        BEGIN_WITH(MyStructABC)
        _.a = 1;
        _.b = 2;
        _.c = 3;
        END_WITH()
    

    【讨论】:

      【解决方案4】:

      在 C++ 中,您可以将代码放在被 pointer 引用的类的方法中。在那里,您可以直接引用成员而不使用指针。把它设为inline,你就可以得到你想要的。

      【讨论】:

      • “make it inline”是什么意思?您可以在另一个函数中声明该方法吗?很奇怪……
      • @Dacav 这个答案只是建议向with 使用的类添加一个方法。成为inline 允许它进入标题,但否则这是一个红鲱鱼。这种方法必须在其原始类中声明,并在任何其他函数之外定义。我在下面的回答对这两个问题都有解决方法。
      【解决方案5】:

      尽管我主要使用带有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++,使用本地 T&amp;T const&amp; 变量来保存对长表达式的引用(在 T&amp; 情况下必须是正确的左值)。为了让它在视觉上脱颖而出,我通常将变量命名为_
      【解决方案6】:

      不,C++ 没有任何这样的关键字。

      【讨论】:

        【解决方案7】:

        C++ 没有这样的特性。许多人认为 Pascal 中的“WITH”是一个问题,因为它会使代码模棱两可且难以阅读,例如很难知道 field1 是指针的成员还是局部变量或其他东西。 Pascal 还允许使用多个带有变量,例如“With Var1,Var2”,这使得它变得更加困难。

        【讨论】:

        • 总有这样一个问题,如果某些东西可以被滥用,就意味着它是坏的。
        • 当然,因为 C++ 中的名称查找非常简单。否 -1,但我认为当您考虑函数的参数依赖查找的情况时,“可以使代码模棱两可”的论点变得平淡无奇,特​​别是对于函数模板的实例化。
        • 我同意它很有用,但我已经看到它被滥用了很多次,所以我不再使用它了。特别是如果 field1 在记录中被重命名,它会咬你,所以你的 with-code 现在突然默默地引用另一个同名的变量。而不是我使用这里建议的其他方法之一:使用具有短名称的局部变量或将其移动到函数/方法。
        • 点赞。最差的语言功能。考虑一下这个可憎的:用 struct1, struct2.substruct3, struct4.substruct5.ptrstruct6^... 看到了。
        • Visual Basic 通过要求属于 'with' 的字段以“.”为前缀来整理语法。 - 它还有助于语法完成等。我们的主持人乔尔做到了:)
        【解决方案8】:

        你能得到的最接近的是method chaining:

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

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

        【讨论】:

        • 这实际上病得很好,因为封装被保留了......感谢您建立联系!
        • 完全不一样!
        【解决方案9】:
        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;
        

        【讨论】:

          【解决方案10】:

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

          【讨论】:

            【解决方案11】:

            首先我听说有人不喜欢'with'。这些规则非常简单明了,与 C++ 或 Java 中的类内部发生的情况没有什么不同。并且不要忽视它可以触发重要的编译器优化。

            【讨论】:

              【解决方案12】:

              以下方法依赖于 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");
                  }
              }
              

              【讨论】:

                【解决方案13】:

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

                【讨论】:

                  【解决方案14】:
                  #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;
                  }
                  

                  努夫说……

                  【讨论】:

                    【解决方案15】:

                    我可以看到一个实例,其中 '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);
                      }
                    }
                    

                    这可能不会超过“with”中提到的所有其他负面影响,但只是作为一个有趣的信息......

                    【讨论】:

                      【解决方案16】:

                      也许你可以:

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

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

                      【讨论】:

                      • 可能应该是auto &amp;p = *pointer,因为您的代码需要一个副本。
                      【解决方案17】:

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

                      【讨论】:

                        【解决方案18】:

                        我对 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);
                            }
                        }
                        

                        【讨论】:

                          【解决方案19】:

                          一个简单的方法如下

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

                          【讨论】:

                          • 这个答案似乎与问题无关?
                          猜你喜欢
                          • 1970-01-01
                          • 2018-01-08
                          • 2012-04-18
                          • 2013-04-16
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          相关资源
                          最近更新 更多