4

Edit:

So this question was misinterpreted to such a ludicrous degree that it has no point anymore. I don't know how, since the question that I actually asked was whether my specific implementation of this—yes, known to be pointless, yes, not remotely resembling idiomatic C++—macro was as good as it could be, and whether it necessarily had to use auto, or if there was a suitable workaround instead. It was not supposed to generate this much attention, and certainly not a misunderstanding of this magnitude. It's pointless to ask respondents to edit their answers, I don't want anybody to lose reputation over this, and there's some good information floating around in here for potential future viewers, so I'm going to arbitrarily pick one of the lower-voted answers to evenly distribute the reputation involved. Move along, nothing to see here.


I saw this question and decided it might be fun to write a with statement in C++. The auto keyword makes this really easy, but is there a better way to do it, perhaps without using auto? I've elided certain bits of the code for brevity.

template<class T>
struct with_helper {

    with_helper(T& v) : value(v), alive(true) {}

    T* operator->() { return &value; }
    T& operator*() { return value; }

    T& value;
    bool alive;

};


template<class T> struct with_helper<const T> { ... };


template<class T> with_helper<T>       make_with_helper(T& value) { ... }
template<class T> with_helper<const T> make_with_helper(const T& value) { ... }


#define with(value) \
for (auto o = make_with_helper(value); o.alive; o.alive = false)

Here's an (updated) usage example with a more typical case that shows the use of with as it is found in other languages.

int main(int argc, char** argv) {

    Object object;

    with (object) {

        o->member = 0;
        o->method(1);
        o->method(2);
        o->method(3);

    }

    with (object.get_property("foo").perform_task(1, 2, 3).result()) {

        std::cout
            << (*o)[0] << '\n'
            << (*o)[1] << '\n'
            << (*o)[2] << '\n';

    }

    return 0;

}

I chose o because it's an uncommon identifier, and its form gives the impression of a "generic thing". If you've got an idea for a better identifier or a more usable syntax altogether, then please do suggest it.

Community
  • 1
  • 1
Jon Purdy
  • 53,300
  • 8
  • 96
  • 166
  • Thanks...Looks interesting and useful. – Robert Harvey Oct 29 '10 at 18:57
  • @Robert Harvey: It's basically just shorthand for `{ [const] auto& o = ...; ... }`, but slightly less ugly. – Jon Purdy Oct 29 '10 at 19:00
  • Do you mean `#define with(value) \ ` instead of `#define with(value, id) \ `? BTW, how about Boost.Typeof? – kennytm Oct 29 '10 at 19:03
  • @KennyTM: Yeah, copying error from testing. Thanks for catching that for me. Boost.Typeof isn't ideal because it doesn't work out of the box with user-defined types, which is one of the most important uses of `with`. – Jon Purdy Oct 29 '10 at 19:20
  • What's the use for this? Doesn't it just add more lines of code and extra code blocks? – Inverse Oct 29 '10 at 19:29
  • @Inverse: Hah. I don't rightly know! People seem to love their `with` statements, though, when they have them. I never said I considered it particularly *useful* as such. It's only a passing thought, but I have to wonder if there's room for improvement. – Jon Purdy Oct 29 '10 at 19:38
  • if you had really long variable names for your vectors people might see the use :P – CiscoIPPhone Oct 29 '10 at 19:41
  • 1
    @CiscolPPhone: Done and done. ;) – Jon Purdy Oct 29 '10 at 19:46
  • @dalle: "...is there a better way to do it, perhaps without using `auto`?" – Jon Purdy Oct 29 '10 at 20:38
  • @Purdy You might be interested in this [stack-exchange proposal](http://area51.stackexchange.com/proposals/11464/code-review?referrer=aWNm_PdciyFqjFW8CUacGw2 "code review"). It's almost ready to begin beta, just needs a few more. – greatwolf Jan 18 '11 at 09:48

4 Answers4

7

If you use auto, why use macros at all?

int main()
{
    std::vector<int> vector_with_uncommonly_long_identifier;

    {
        auto& o = vector_with_uncommonly_long_identifier;

        o.push_back(1);
        o.push_back(2);
        o.push_back(3);
    }

    const std::vector<int> constant_duplicate_of_vector_with_uncommonly_long_identifier
        (vector_with_uncommonly_long_identifier);

    {
        const auto& o = constant_duplicate_of_vector_with_uncommonly_long_identifier;

        std::cout
            << o[0] << '\n'
            << o[1] << '\n'
            << o[2] << '\n';
    }

    {
        auto o = constant_duplicate_of_vector_with_uncommonly_long_identifier.size();
        std::cout << o <<'\n';
    }
}

EDIT: Without auto, just use typedef and references.

int main()
{
    typedef std::vector<int> Vec;

    Vec vector_with_uncommonly_long_identifier;

    {
        Vec& o = vector_with_uncommonly_long_identifier;

        o.push_back(1);
        o.push_back(2);
        o.push_back(3);
    }
}
dalle
  • 18,057
  • 5
  • 57
  • 81
6

?? attempted vb syntax into C++

with says do all the things in the following block by default referencing the object I've said to do it with right? Executes a series of statements making repeated reference to a single object or structure.

with(a)
 .do
 .domore
 .doitall

so how is the example giving you the same syntax?

to me examples of why to use a with where multiple de referencess

so rather than

book.sheet.table.col(a).row(2).setColour
book.sheet.table.col(a).row(2).setFont
book.sheet.table.col(a).row(2).setText
book.sheet.table.col(a).row(2).setBorder

you have

with( book.sheet.table.col(a).row(2) )
  .setColour
  .setFont
  .setText
  .setBorder

seems just as easy, and more common syntax in C++ to

cell& c = book.sheet.table.col(a).row(2);
c.setColour
c.setFont
c.setText
c.setBorder
Greg Domjan
  • 13,943
  • 6
  • 43
  • 59
  • But, a with statement in python does "initialization" ( __enter__ ) and "cleanup" ( __exit__ ). Is it possible to do something similar to that in c++ ? Link: https://docs.python.org/3/reference/compound_stmts.html#the-with-statement – Jayanth Feb 01 '23 at 05:11
3

For C++0x (which you're assuming):

int main() {

    std::vector<int> vector_with_uncommonly_long_identifier;

    {
        auto& o = vector_with_uncommonly_long_identifier;

        o.push_back(1);
        o.push_back(2);
        o.push_back(3);

    }
}
Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
0

Why not just use a good lambda?

auto func = [&](std::vector<int>& o) {
};
func(vector_with_a_truly_ridiculously_long_identifier);

The simple fact is that if your identifiers are so long, that you can't type them out every time, use a reference, function, pointer, etc to solve this problem, or better, refactor the name. Statements like this (e.g. using() in C#) have additional side effects (deterministic cleanup, in my example). Your statement in C++ has no notable actual benefits, since it doesn't actually invoke any additional behaviour against just writing the code out.

Puppy
  • 144,682
  • 38
  • 256
  • 465
  • The problem with this approach is that it makes the code harder to read -- you have to jump downwards to the bottom of the lambda definition to find the object being acted on, then jump back up to figure out what the code is doing to that object, instead of just reading the code in order. – Adam Rosenfield Oct 29 '10 at 21:20
  • 1
    @Adam Rosenfield: It also has the advantage that func could be dynamically passed around through the use of std::function. Moreover, the OP's entire not-really-a-problem is ridiculous. Are you expecting a non-ridiculous solution? – Puppy Oct 29 '10 at 22:00
  • @Adam Rosenfield, @DeadMG: Jeez, I just get the bug to know if the obviously, deliberately useless macro I wrote was written as well as it could've been, and a slew of irrelevant answers ensue. I don't want to delete the question, but there's no point to it anymore. – Jon Purdy Oct 30 '10 at 01:46