// bslma_exceptionguard.h                                             -*-C++-*-
#ifndef INCLUDED_BSLMA_EXCEPTIONGUARD
#define INCLUDED_BSLMA_EXCEPTIONGUARD

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@PURPOSE: Provide a check that objects throwing exceptions do not change.
//
//@CLASSES:
//  bslma::ExceptionGuard: guard to check an object state has not changed
//
//@SEE_ALSO: bslma_testallocator
//
//@DESCRIPTION: This component provides a class, `bslma::ExceptionGuard`, that
// can be used to ASSERT if an object changes state when a method fails by
// throwing an exception.  This is often used to validate the strong exception
// safety guarantee in a test driver, usually with the test support macros
// provided by the component `bslma_testallocator`, such as
// `BSLMA_TESTALLOCATOR_EXCEPTION_TEST_BEGIN`.  The object under test must be
// CopyConstructible, and support the extended copy constructor taking an
// allocator.  Note that this may be a generalised STL allocator, conforming to
// the Allocator requirements of the C++ standard, rather than just a
// `bslma::Allocator`.  This allows for testing standard library components
// such as those in `bsl`.
//
// As the constructor must make a copy of the object under test, this class
// should not be used in a test driver until after the extended copy
// constructor has been proven tested.  Similarly, the destructor asserts that
// the value has not changed using `operator==`, which should also be confirmed
// as correct before relying on this class in a test driver.  Finally, the
// `resetvalue` method should not be used prior to validating the copy-
// assignment operator.
//
///Usage
///-----
// TBD ...

#include <bslscm_version.h>

#include <bsls_assert.h>

namespace BloombergLP {

namespace bslma {

class Allocator;

                            // ====================
                            // class ExceptionGuard
                            // ====================

/// This class provide a mechanism to verify the strong exception guarantee
/// in exception-throwing code.  On construction, this class stores the a
/// copy of an object of the parameterized type `OBJECT` and the address of
/// that object.  On destruction, if `release` was not invoked, it will
/// verify the value of the object is the same as the value of the copy
/// create on construction.  This class requires the copy constructor and
/// `operator ==` to be tested before use.
template <class OBJECT>
class ExceptionGuard {

    // DATA
    int           d_line;      // the line number at construction
    OBJECT        d_copy;      // copy of the object being tested
    const OBJECT *d_object_p;  // address of the original object

  public:
    // CREATORS

    /// Create the exception guard for the specified `object` at the
    /// specified `line` number.  Optionally, specify `basicAllocator` used
    /// to supply memory.
    ExceptionGuard(const OBJECT *object,
                   int           line,
                   Allocator    *basicAllocator = 0);

    /// Create the exception guard for the specified `object` at the
    /// specified `line` number.  Optionally, specify `basicAllocator` used
    /// to supply memory.
    template <class ALLOCATOR>
    ExceptionGuard(const OBJECT     *object,
                   int               line,
                   const ALLOCATOR&  basicAllocator);

    /// Destroy the exception guard.  If the guard was not released, verify
    /// that the state of the object supplied at construction has not
    /// change.
    ~ExceptionGuard();

    // MANIPULATORS

    /// Release the guard from verifying the state of the object.
    void release();

    /// Reset the expected state of the guarded object, if an exception
    /// should propagate past this guard, to the specified `value`, which is
    /// set from the specified `line`.
    void resetValue(const OBJECT& value, int line);
};

// ============================================================================
//                          INLINE DEFINITIONS
// ============================================================================


                       // --------------------
                       // class ExceptionGuard
                       // --------------------

// CREATORS
template <class OBJECT>
inline
ExceptionGuard<OBJECT>::ExceptionGuard(const OBJECT *object,
                                       int           line,
                                       Allocator    *basicAllocator)
: d_line(line)
, d_copy(*object, basicAllocator)
, d_object_p(object)
{
}

template <class OBJECT>
template <class ALLOCATOR>
inline
ExceptionGuard<OBJECT>::ExceptionGuard(const OBJECT     *object,
                                       int               line,
                                       const ALLOCATOR&  basicAllocator)
: d_line(line)
, d_copy(*object, basicAllocator)
, d_object_p(object)
{
}

template <class OBJECT>
ExceptionGuard<OBJECT>::~ExceptionGuard()
{
    if (d_object_p) {
        // This test is tricky, as it is typically triggered while an exception
        // is active, and so should not have an assert handler that in turn
        // throws an exception.  Note that as this assertion is the whole
        // purpose of the class, we use 'BSLS_ASSERT_OPT' so that it is active
        // in most build modes.
        BSLS_ASSERT_OPT(d_copy == *d_object_p);
    }
}

// MANIPULATORS
template <class OBJECT>
inline
void ExceptionGuard<OBJECT>::release()
{
    d_object_p = 0;
}

template <class OBJECT>
inline
void ExceptionGuard<OBJECT>::resetValue(const OBJECT& value, int line)
{
    d_copy = value;
    d_line = line;
}

}  // close package namespace

}  // close enterprise namespace

#endif

// ----------------------------------------------------------------------------
// Copyright 2013 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------
