14

I am looking for an elegant solution for implementing the equivalent of the C# using statement in C++. Ideally the resultant syntax should be simple to use and read.

C# Using statement details are here - http://msdn.microsoft.com/en-us/library/yh598w02(v=vs.80).aspx

I am not sure whether the solution would be to use function pointers with destructors on classes, some form of clever template programming or even meta template programming. Basically I do not know where to start with this...

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
adamretter
  • 3,885
  • 2
  • 23
  • 43
  • 14
    I've been told RAII covers this. – Anthony Pegram Feb 03 '12 at 17:57
  • 1
    As Anthony said, using RAII will automatically do this for you, and imho, it makes the code far cleaner than the using pattern. – Mranz Feb 03 '12 at 18:00
  • 4
    From my point of view, the `using` keyword is just a mediocre remedy for not having RAII in the first place. So, thou shalt not seek to emulate it. – Andre Feb 03 '12 at 18:01
  • @Andre It is a little bit harder. I suppose that the C# compiler could treat a scoped IDisposable similar to RAII, but I am not sure how it would know that it is safe to dispose the object. Maybe check the ref count when the object leaves scope and immediately call dispose if 0? – Mranz Feb 03 '12 at 18:04
  • @Mranz What are you talking about? Dispose is guaranteed to be called when the `using` scope ends. Read all about it: http://msdn.microsoft.com/en-us/library/yh598w02.aspx – David Heffernan Feb 03 '12 at 18:05
  • 2
    @DavidHeffernan I am talking about not having to use the using scope. – Mranz Feb 03 '12 at 18:06
  • The `using` keyword is error prone and has been added to C# as an afterthought. It is very easy to write wrong code by forgetting it. C++ has a better mechanism which does not require you to write anything: destructors ! – Alexandre C. Feb 03 '12 at 18:14
  • @mranz I see. That's not going to fly at all. Also .net gc does not use ref counts. – David Heffernan Feb 03 '12 at 18:20
  • @DavidHeffernan Right, it walks the stack. Either way, I think they can do better. – Mranz Feb 03 '12 at 18:34
  • @alex perhaps you don't recognise the different motivations with gc – David Heffernan Feb 03 '12 at 18:36
  • @DavidHeffernan: I do recognize that nondeterministic destruction of objects makes it hard to bind events to destruction time. However, the `using` keyword and the `IDisposable` interface *are* error prone. Forget to write `using` (you won't get even a warning) and all can go wrong. Garbage collected functional languages like Scheme have proper RAII (eg. `call-with-port`). – Alexandre C. Feb 03 '12 at 20:25
  • @AlexandreC. Surely it's possible to forget to use call-with-port? Or is there something extra. Surely you need to somehow tell the environment about unmanaged resources so they can be treated specially. – David Heffernan Feb 03 '12 at 20:33

7 Answers7

34

You don't need to implement this in C++ because the standard pattern of RAII already does what you need.

{
    ofstream myfile;
    myfile.open("hello.txt");
    myfile << "Hello\n";
}

When the block scope ends, myfile is destroyed which closes the file and frees any resources associated with the object.

The reason the using statement exists in C# is to provide some syntactic sugar around try/finally and IDisposable. It is simply not needed in C++ because the two languages differ and the problem is solved differently in each language.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • 2
    This is far superior to the C# `using`, and it also works with .NET objects in C++/CLI. The C++ syntax is far better because although you still have to remember to use the value semantics syntax: (1) You can use it for every type, whether or not it implements `IDisposable`, or not, or for interfaces, different objects may or may not, and (2) It works for class members. – Ben Voigt Feb 04 '12 at 00:18
  • 1
    I think it's not a matter of better or worse, it's a matter of what tools you have to deal with the problem in your environment. C++ uses heap as memory management and also stack, but it doesn't have garbage collection, so that's the difference, GC delayed memory management, which has it's own advantages, so what should I do to work in that context? we're not different sport teams here, we're developers dealing with different technologies... – Eugenio Miró Dec 08 '14 at 15:28
  • I don't see how this is the accepted answer, and by so many up-votes, since RAII is just a design principal. Isn't it setting yourself up for failure to say that RAII takes care of everything, all the time? The better answer and more analogous to C#'s `using` statement seems to be [Smart Pointer](https://msdn.microsoft.com/en-us/library/hh279674.aspx) usage, more specifically, [std::unique_ptr](http://en.cppreference.com/w/cpp/memory/unique_ptr) as mentioned by another answer here. The smart pointer implements a RAII wrapper for you on the calling side of allocation, so analogous to `using`. – u8it May 22 '18 at 17:10
  • As an alternative that emphasizes your RAII object better: `if(std::ofstream myfile("hello.txt"); true) { /* code */ };`. You can see how if you put it on multiple lines, the object is put apart from the code inside the curly braces, same as in C#: `using (StreamReader reader = File.OpenText("hello.txt")) { // code }`. – KulaGGin Apr 22 '23 at 11:16
3

A more verbose RAII pattern that resembles C#'s using statement can be accomplished with a simple macro.

#define Using(what, body) { what; body; }

Using(int a=9,
{
    a++;
})

a++; // compile error, a has gone out of scope here

Note we must use a capital "Using" to avoid a collision with C++'s built in "using" statement which obviously has a different meaning.

Dan Watkins
  • 918
  • 9
  • 25
  • I like this becasue I like the explicit nature of 'using'. As for the macro itself, I like this one better: https://tedclancy.wordpress.com/2015/07/04/raii-helper-macro/. – Dina Aug 26 '15 at 12:26
3

First, we have to define a Closeable/Disposable public interface:

#include <iostream>

using namespace std;


class Disposable{
private:
    int disposed=0;
public:
    int notDisposed(){
        return !disposed;
    }
    
    void doDispose(){
        disposed = true;
        dispose();
    }
    
    virtual void dispose(){}
    
};

Then we should define a macro for the using keyword:

#define using(obj) for(Disposable *__tmpPtr=obj;__tmpPtr->notDisposed();__tmpPtr->doDispose())

and; here is an example application:

class Connection : public Disposable {
    
private:
    Connection *previous=nullptr;
public:
    static Connection *instance;
    
    Connection(){
        previous=instance;
        instance=this;
    }
    
    void dispose(){
        delete instance;
        instance = previous;
    }
};

Connection *Connection::instance = nullptr;

int Execute(const char* query){
    if(Connection::instance == nullptr){
        cout << "------- No Connection -------" << endl;
        cout << query << endl;
        cout << "------------------------------" << endl;
        cout << endl;
        
        return -1;//throw some Exception
    }
    
    cout << "------ Execution Result ------" << endl;
    cout << query << endl;
    cout << "------------------------------" << endl;
    cout << endl;
    
    return 0;
}

int main(int argc, const char * argv[]) {
    
    using(new Connection())
    {
        Execute("SELECT King FROM goats");//in the scope 
    }
    
    Execute("SELECT * FROM goats");//out of the scope
    
}

But if you want to delete variables automatically from memory, you can simply use braces {}; therefore, every variable inside of the scope will be removed at the end of the scope. here is an example:

int main(int argc, const char * argv[]) {
    {
        int i=23;
    } 
    
    // the variable i has been deleted from the momery at here.
} 
Siamand
  • 1,080
  • 10
  • 19
3
    #define USING(...) if(__VA_ARGS__; true)

        USING(int i = 0)
        USING(std::string s = "0")
        {
            Assert::IsTrue(i == 0, L"Invalid result", LINE_INFO());
            Assert::IsTrue(s == "0", L"Invalid result", LINE_INFO());
        }
        //i = 1; // error C2065: 'i': undeclared identifier
        //s = "1"; //error C2065: 's': undeclared identifier
jenkas
  • 872
  • 14
  • 16
3

I'd take a look at using std::auto_ptr<> to handle cleanup of any instances allocated and assigned to a pointer within a particular scope -- otherwise, any variables declared within a specific scope will simply be destructed when exiting said scope.

{
    SomeClass A;
    A.doSomething();
} // The destructor for A gets called after exiting this scope here

{
    SomeClass* pA = new SomeClass();
    std::auto_ptr<SomeClass> pAutoA(pA);
    pAutoA->doSomething();
} // The destructor for A also gets called here, but only because we
  // declared a std::auto_ptr<> and assigned A to it within the scope.

See http://en.wikipedia.org/wiki/Auto_ptr for a little more information on std::auto_ptr<>

hatboyzero
  • 1,929
  • 1
  • 21
  • 44
  • Good to know, thanks. Most days, I usually stick to the Boost libraries anyways... – hatboyzero Feb 03 '12 at 18:13
  • There are other [Smart Pointers](https://msdn.microsoft.com/en-us/library/hh279674.aspx), in addition to auto_ptr and unique_ptr. – u8it May 22 '18 at 17:00
0

As an alternative to other answers that emphasizes your RAII object, doesn't require macros and has a very similar syntax to C#:

if(std::ofstream myfile("hello.txt"); true) {
    // code 
};

The object is put apart from the code inside the curly braces, same as in C#:

using (StreamReader reader = File.OpenText("hello.txt")) {
    // code 
}

A separate keyword like in C#, instead of if would be better, of course.

With a macro, similar to C# syntax:

#define Using(what) if(what; true)

Using(std::ofstream myfile("hello.txt")) {
        // code 
};

Requires C++ 17.

KulaGGin
  • 943
  • 2
  • 12
  • 27