开发者

Does C++ have "with" keyword like Pascal?

with keyword in Pascal can be use to quick access the field of a record. Anybody knows if C++ has anything similar to开发者_运维问答 that?

Ex: I have a pointer with many fields and i don't want to type like this:

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

what I really want is something like this in C++:

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


Probably the closest you can get is this: (this is just an academic exercise. Of course, you can't use any local variables in the body of these artificial with blocks!)

struct Bar {
    int field;
};

void foo( Bar &b ) {
    struct withbar : Bar { void operator()() {
        cerr << field << endl;
    }}; static_cast<withbar&>(b)();
}

Or, a bit more demonically,

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

or in 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;


no there is no such keyword.


I like to use:

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

    #define END_WITH() }

Example:

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


In C++, you can put code in a method of the class being reference by pointer. There you can directly reference the members without using the pointer. Make it inline and you pretty much get what you want.


Even though I program mostly in Delphi which has a with keyword (since Delphi is a Pascal derivative), I don't use with. As others have said: it saves a bit on typing, but reading is made harder.

In a case like the code below it might be tempting to use 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;

Using with this looks like this

with cxGrid.DBTableView.ViewData.Records do
begin
  FieldByName('foo').Value = 1;
  FieldByName('bar').Value = 2;
  FieldByName('baz').Value = 3;
end;

I prefer to use a different technique by introducing an extra variable pointing to the same thing with would be pointing to. Like this:

var lRecords: TDataSet;

lRecords := cxGrid.DBTableView.ViewData.Records;

lRecords.FieldByName('foo').Value = 1;
lRecords.FieldByName('bar').Value = 2;
lRecords.FieldByName('baz').Value = 3;

This way there is no ambiguity, you save a bit on typing and the intent of the code is clearer than using with


No, C++ does not have any such keyword.


C++ does not have a feature like that. And many consider "WITH" in Pascal to be a problem because it can make the code ambiguous and hard to read, for example it hard to know if field1 is a member of pointer or a local variable or something else. Pascal also allows multiple with-variables such as "With Var1,Var2" which makes it even harder.


The closest you can get is method chaining:

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

for setting multiple fields and using for namespaces.


with (OBJECT) {CODE}

There is no such thing in C++.
You can put CODE as is into a method of OBJECT, but it is not always desirable.

With C++11 you can get quite close by creating alias with short name for OBJECT.
For example code given in question it will look like so:

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

(The surrounding curly braces are used to limit visibility of alias _ )

If you use some field very often you can alias it directly:

auto &field = pointer->field;
// Even shorter alias:
auto &_ = pointer->busy_field;


No, there is no with keyword in C/C++.

But you can add it with some preprocessor code:

/* 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__))

Usage:

with (someVeryLongObjectNameOrGetterResultOrWhatever) {
    if (it)
        it->...
    ...
}

with (someVeryLongObjectNameOrGetterResultOrWhatever, myObject) {
    if (myObject)
        myObject->...
    ...
}

Simplified unoverloaded definitions (choose one):

unnamed (Kotlin style it):

#define with(object) \
    for (typeof(object) &it = (object), *__i = 0; __i < (void*)1; ++__i)

named:

#define with(object, as) \
    for (typeof(object) &as = (object), *__i = 0; __i < (void*)1; ++__i)

Of course the for loop always has only a single pass and will be optimized out by the compiler.


First I've heard that anybody doesn't like 'with'. The rules are perfectly straightforward, no different from what happens inside a class in C++ or Java. And don't overlook that it can trigger a significant compiler optimization.


The following approach relies on Boost. If your compiler supports C++0x's auto then you can use that and get rid of the Boost dependence.

Disclaimer: please don't do this in any code that must be maintained or read by someone else (or even by yourself in a few months):

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


Having written numerous parsers, this seems like a dead simple list look up for the named object, either static or dynamic. Further, I have never seen a situation where the compiler did not correctly identify the missing object and type, so all those lame excuses for not allowing a WITH ...ENDWITH construction would seem to be a lot of hooey. For the rest of us prone to long object names one workaround is to create simple defines. Couldn't resist, suppose I have:

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

Nuf said ...


I can see one instance where 'with' is actually useful.

In methods for recursive data structures, you often have the case:

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

errors caused by typos like this are very hard to find.

With 'with' you could write

void A::method()
{
  for (A* node = this; node; node = node->next) with (node) {
    abc(value1);
    def(value2);
    xyz(value3);
  }
}

This probably doesn't outweight all the other negatives mentioned for 'with', but just as an interesting info...


Maybe you can:

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

Or create a small program that will understand with statements in C++ and translate them to some form of a valid C++.


I too came from the Pascal world..... .....and I also LOVE Python's use of with (basically having an automatic try/finally):

  with open(filename, "r") as file:
    for line in file:
      if line.startswith("something"):
        do_more()

That acts like a smart ptr object. It does not go into the block if the open failed; and when leaving the block, the file if closed.

Here is a sample very close to Pascal while also supporting Python's usage (assuming you have a smart object with destructor cleanup); You need newer C++ standard compilers for it to work.

    // 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;
    }

And if you want even closer:

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

The (quick and dirty) supporting code for this example:

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


I was lamenting to PotatoSwatter (currently accepted answer) that I could not access variables declared in the enclosing scope with that solution. I tried to post this in a comment response to PotatoSwatter, but it's better as a whole post. It's all a bit over the top, but the syntax sugar is pretty nice!

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


A simple way to do this is as follows

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

I've been writing python all day long and just scrapped this down before anyone takes me to the cleaners. In a larger program this is an example of how to keep a reliable count for something.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜