Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is "sentry object" in C++?

Tags:

c++

idioms

I answered this question, and Potatoswatter answered too as

The modern C++ equivalent would be a sentry object: construct it at the beginning of a function, with its constructor implementing call(), and upon return (or abnormal exit), its destructor implements

I am not familiar with using sentry objects in C++. I thought they were limited to input and output streams.

Could somebody explain to me about C++ sentry objects as well as how to use them as an around interceptor for one or more methods in a class ?

i.e. How to do this ?

Sentry objects are very similar indeed. On the one hand they require explicit instantiation (and being passed this) but on the other hand you can add to them so that they check not only the invariants of the class but some pre/post conditions for the function at hand.

like image 202
Romain Hippeau Avatar asked Apr 22 '10 12:04

Romain Hippeau


People also ask

What is sentry object?

Sentry objects are very similar indeed. On the one hand they require explicit instantiation (and being passed this) but on the other hand you can add to them so that they check not only the invariants of the class but some pre/post conditions for the function at hand.

What is Sentry C++?

sentry-native/0.5. 0 The Sentry Native SDK is an error and crash reporting client for native applications, optimized for C and C++. Sentry allows to add tags, breadcrumbs and arbitrary custom context to enrich error reports.


1 Answers

Sentry object is a pattern, but I'm not sure which one of those below (maybe all).

C++ programs often heavily rely on knowledge when exactly an object (possibly of a user-defined class) is destroyed, i.e. when its destructor is called. This is not the case for languages with garbage collection.

This technique is used, for example, to embrace "Resource Acquisition Is Initialization" paradigm: you acquire resources when an object constructor is called, and the compiler automatically calls its destructor to free resources in both normal and abnormal (exceptional) situations (check this question).

Common places where you can utilize the knowledge of construction/destruction timing are

  • Blocks: a destructor for "stack-allocated" object is called at the end of the block

    void function()
    {  Class foo = Object(resource);
       other_operations();
    }  // destructor for foo is called here
    
  • Function calls: "stack-allocation" also happens when you call a function

    void function()
    {  another_function ( Class(resource)  );
       // destructor for the unnamed object is called
       // after another_function() returns (or throws)
       other_operations();
    }
    
  • Construction/Destruction of the containing object:

    class Foo
    {  Class sentry;
       public: Foo()
       { // Constructor for sentry is called here
          something();
       }        
       public: ~Foo()
       {
          something();
       }  // destructor for sentry is called here
    };
    

In STL there's a class called sentry (more exactly, istream::sentry), which implements the third pattern of those described above. So I think that is what some programmers refer to as "sentry object".

But in fact any of the above objects of class Class may be called "sentry object". They're "sentry" because they ensure that these elusive object destructors are not missed even if something throws an exception (so they are like guardians of the block/class, of sorts).

More sentry object examples are in that RAII question.


You can see a relation to aspect-oriented programming; these objects are something like "aspects", with cutpoints "at the beginning/ending of the enclosing block", "at construction/destruction of containing object" etc. But these "aspects" have to present in the code they aspectate. So they're less "aspective" compared to the original call/return functionality; instead, a sentry object should be inserted to each function of the class:

class X{
  struct Sentry {
     Sentry() { /* call() */}
    ~Sentry() { /* return() */};
  };

  void member_function()
  { Sentry();
    /* operations */
  }

  void another_member_function()
  { Sentry();
    /* operations */
  }
};
like image 101
P Shved Avatar answered Nov 16 '22 00:11

P Shved