summaryrefslogtreecommitdiff
path: root/boost/contract/detail/condition/cond_inv.hpp
blob: b4c6c03133bbeae8e047e38380162668ebdc6ff3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232

#ifndef BOOST_CONTRACT_DETAIL_COND_INV_HPP_
#define BOOST_CONTRACT_DETAIL_COND_INV_HPP_

// Copyright (C) 2008-2018 Lorenzo Caminiti
// Distributed under the Boost Software License, Version 1.0 (see accompanying
// file LICENSE_1_0.txt or a copy at http://www.boost.org/LICENSE_1_0.txt).
// See: http://www.boost.org/doc/libs/release/libs/contract/doc/html/index.html

#include <boost/contract/core/exception.hpp>
#include <boost/contract/core/config.hpp>
#include <boost/contract/detail/condition/cond_post.hpp>
#ifndef BOOST_CONTRACT_NO_INVARIANTS
    #include <boost/contract/core/access.hpp>
    #include <boost/type_traits/add_pointer.hpp>
    #include <boost/type_traits/is_volatile.hpp>
    #include <boost/mpl/vector.hpp>
    #include <boost/mpl/transform.hpp>
    #include <boost/mpl/for_each.hpp>
    #include <boost/mpl/copy_if.hpp>
    #include <boost/mpl/eval_if.hpp>
    #include <boost/mpl/not.hpp>
    #include <boost/mpl/and.hpp>
    #include <boost/mpl/placeholders.hpp>
    #include <boost/utility/enable_if.hpp>
    #ifndef BOOST_CONTRACT_PERMISSIVE
        #include <boost/function_types/property_tags.hpp>
        #include <boost/static_assert.hpp>
    #endif
#endif

namespace boost { namespace contract { namespace detail {

template<typename VR, class C>
class cond_inv : public cond_post<VR> { // Non-copyable base.
    #if     !defined(BOOST_CONTRACT_NO_INVARIANTS) && \
            !defined(BOOST_CONTRACT_PERMISSIVE)
        BOOST_STATIC_ASSERT_MSG(
            (!boost::contract::access::has_static_invariant_f<
                C, void, boost::mpl:: vector<>
            >::value),
            "static invariant member function cannot be mutable "
            "(it must be static instead)"
        );
        BOOST_STATIC_ASSERT_MSG(
            (!boost::contract::access::has_static_invariant_f<
                C, void, boost::mpl::vector<>,
                        boost::function_types::const_non_volatile
            >::value),
            "static invariant member function cannot be const qualified "
            "(it must be static instead)"
        );
        BOOST_STATIC_ASSERT_MSG(
            (!boost::contract::access::has_static_invariant_f<
                C, void, boost::mpl::vector<>,
                        boost::function_types::volatile_non_const
            >::value),
            "static invariant member function cannot be volatile qualified "
            "(it must be static instead)"
        );
        BOOST_STATIC_ASSERT_MSG(
            (!boost::contract::access::has_static_invariant_f<
                C, void, boost::mpl::vector<>,
                        boost::function_types::cv_qualified
            >::value),
            "static invariant member function cannot be const volatile "
            "qualified (it must be static instead)"
        );

        BOOST_STATIC_ASSERT_MSG(
            (!boost::contract::access::has_invariant_s<
                C, void, boost::mpl::vector<>
            >::value),
            "non-static invariant member function cannot be static "
            "(it must be either const or const volatile qualified instead)"
        );
        BOOST_STATIC_ASSERT_MSG(
            (!boost::contract::access::has_invariant_f<
                C, void, boost::mpl::vector<>,
                        boost::function_types::non_cv
            >::value),
            "non-static invariant member function cannot be mutable "
            "(it must be either const or const volatile qualified instead)"
        );
        BOOST_STATIC_ASSERT_MSG(
            (!boost::contract::access::has_invariant_f<
                C, void, boost::mpl::vector<>,
                        boost::function_types::volatile_non_const
            >::value),
            "non-static invariant member function cannot be volatile qualified "
            "(it must be const or const volatile qualified instead)"
        );
    #endif

public:
    // obj can be 0 for static member functions.
    explicit cond_inv(boost::contract::from from, C* obj) :
        cond_post<VR>(from)
        #ifndef BOOST_CONTRACT_NO_CONDITIONS
            , obj_(obj)
        #endif
    {}
    
protected:
    #ifndef BOOST_CONTRACT_NO_ENTRY_INVARIANTS
        void check_entry_inv() { check_inv(true, false, false); }
        void check_entry_static_inv() { check_inv(true, true, false); }
        void check_entry_all_inv() { check_inv(true, false, true); }
    #endif
    
    #ifndef BOOST_CONTRACT_NO_EXIT_INVARIANTS
        void check_exit_inv() { check_inv(false, false, false); }
        void check_exit_static_inv() { check_inv(false, true, false); }
        void check_exit_all_inv() { check_inv(false, false, true); }
    #endif

    #ifndef BOOST_CONTRACT_NO_CONDITIONS
        C* object() { return obj_; }
    #endif

private:
    #ifndef BOOST_CONTRACT_NO_INVARIANTS
        // Static, cv, and const inv in that order as strongest qualifier first.
        void check_inv(bool on_entry, bool static_only, bool const_and_cv) {
            if(this->failed()) return;
            try {
                // Static members only check static inv.
                check_static_inv<C>();
                if(!static_only) {
                    if(const_and_cv) {
                        check_cv_inv<C>();
                        check_const_inv<C>();
                    } else if(boost::is_volatile<C>::value) {
                        check_cv_inv<C>();
                    } else {
                        check_const_inv<C>();
                    }
                }
            } catch(...) {
                if(on_entry) {
                    this->fail(&boost::contract::entry_invariant_failure);
                } else this->fail(&boost::contract::exit_invariant_failure);
            }
        }
        
        template<class C_>
        typename boost::disable_if<
                boost::contract::access::has_const_invariant<C_> >::type
        check_const_inv() {}
        
        template<class C_>
        typename boost::enable_if<
                boost::contract::access::has_const_invariant<C_> >::type
        check_const_inv() { boost::contract::access::const_invariant(obj_); }
        
        template<class C_>
        typename boost::disable_if<
                boost::contract::access::has_cv_invariant<C_> >::type
        check_cv_inv() {}

        template<class C_>
        typename boost::enable_if<
                boost::contract::access::has_cv_invariant<C_> >::type
        check_cv_inv() { boost::contract::access::cv_invariant(obj_); }
        
        template<class C_>
        typename boost::disable_if<
                boost::contract::access::has_static_invariant<C_> >::type
        check_static_inv() {}
        
        template<class C_>
        typename boost::enable_if<
                boost::contract::access::has_static_invariant<C_> >::type
        check_static_inv() {
            // SFINAE HAS_STATIC_... returns true even when member is inherited
            // so extra run-time check here (not the same for non static).
            if(!inherited<boost::contract::access::has_static_invariant,
                    boost::contract::access::static_invariant_addr>::apply()) {
                boost::contract::access::static_invariant<C_>();
            }
        }

        // Check if class's func is inherited from its base types or not.
        template<template<class> class HasFunc, template<class> class FuncAddr>
        struct inherited {
            static bool apply() {
                try {
                    boost::mpl::for_each<
                        // For now, no reason to deeply search inheritance tree
                        // (SFINAE HAS_STATIC_... already fails in that case).
                        typename boost::mpl::transform<
                            typename boost::mpl::copy_if<
                                typename boost::mpl::eval_if<boost::contract::
                                        access::has_base_types<C>,
                                    typename boost::contract::access::
                                            base_types_of<C>
                                ,
                                    boost::mpl::vector<>
                                >::type,
                                HasFunc<boost::mpl::_1>
                            >::type,
                            boost::add_pointer<boost::mpl::_1>
                        >::type
                    >(compare_func_addr());
                } catch(signal_equal const&) { return true; }
                return false;
            }

        private:
            class signal_equal {}; // Except. to stop for_each as soon as found.

            struct compare_func_addr {
                template<typename B>
                void operator()(B*) {
                    // Inherited func has same addr as in its base.
                    if(FuncAddr<C>::apply() == FuncAddr<B>::apply()) {
                        throw signal_equal();
                    }
                }
            };
        };
    #endif

    #ifndef BOOST_CONTRACT_NO_CONDITIONS
        C* obj_;
    #endif
};

} } } // namespace

#endif // #include guard