sparrow ..
Loading...
Searching...
No Matches
contracts.hpp
Go to the documentation of this file.
1// Copyright 2024 Man Group Operations Limited
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// NOTE: This is based upon v0.3.0 of NoContracts library
16// https://github.com/Klaim/nocontracts/tree/0ffc183f7527213e3f0a57b8dae9befa7c0ca02c
17// Modifications: renamed macros
18
19#pragma once
20#include <csignal>
21#include <cstdio>
22
23#if !defined(SPARROW_CONTRACTS_THROW_ON_FAILURE)
24# define SPARROW_CONTRACTS_THROW_ON_FAILURE 0
25#endif
26
27// Include sparrow exception if throwing is enabled
28#if SPARROW_CONTRACTS_THROW_ON_FAILURE == 1
29# include <string>
30
31# include "sparrow_exception.hpp"
32
33#endif
34
35
37// Possible bits used to compose the behavior:
38
39// if not specified by the user but available, use std::print
40#if not defined(SPARROW_CONTRACTS_USE_STD_PRINT) and not defined(SPARROW_CONTRACTS_USE_STD_FORMAT)
41# if __cplusplus >= 202002L
42# include <version>
43# ifdef __cpp_lib_print
44# define SPARROW_CONTRACTS_USE_STD_PRINT 1
45# endif
46# endif
47#else
48// The option is defined, but is it supported? If not we fail.
49# if defined(SPARROW_CONTRACTS_USE_STD_PRINT) and not defined(__cpp_lib_print)
50# error "std::print usage is requested but not available"
51# endif
52#endif
53
54// if not specified by the user but available and std::print is not already forced, use std::format
55#if not defined(SPARROW_CONTRACTS_USE_STD_FORMAT) and not defined(SPARROW_CONTRACTS_USE_STD_PRINT)
56# if __cplusplus >= 202002L
57# include <version>
58# ifdef __cpp_lib_format
59# define SPARROW_CONTRACTS_USE_STD_FORMAT 1
60# endif
61# endif
62// The option is defined, but is it supported? If not we fail.
63# if defined(SPARROW_CONTRACTS_USE_STD_FORMAT) and not defined(__cpp_lib_format)
64# error "std::format usage is requested but not available"
65# endif
66#endif
67
68// user specified to use neither std::format nor std::print, but C's formatting
69#if defined(SPARROW_CONTRACTS_USE_CFORMAT) && SPARROW_CONTRACTS_USE_CFORMAT == 1
70# ifdef SPARROW_CONTRACTS_USE_STD_FORMAT
71# undef SPARROW_CONTRACTS_USE_STD_FORMAT
72# endif
73# ifdef SPARROW_CONTRACTS_USE_STD_PRINT
74# undef SPARROW_CONTRACTS_USE_STD_PRINT
75# endif
76#endif
77
78// clang-format off
79#if defined(__GNUC__)
80# define SPARROW_CONTRACTS_IGNORE_WARNINGS \
81 _Pragma("GCC diagnostic push") \
82 _Pragma("GCC diagnostic ignored \"-Wall\"") \
83 _Pragma("GCC diagnostic ignored \"-Wformat-security\"")
84# define SPARROW_CONTRACTS_RESTORE_WARNINGS \
85 _Pragma("GCC diagnostic pop")
86#elif defined(__clang__)
87# define SPARROW_CONTRACTS_IGNORE_WARNINGS \
88 _Pragma("clang diagnostic push") \
89 _Pragma("clang diagnostic ignored \"-Weverything\"")
90# define SPARROW_CONTRACTS_RESTORE_WARNINGS \
91 _Pragma("clang diagnostic pop")
92#elif defined(_MSC_VER)
93# define SPARROW_CONTRACTS_IGNORE_WARNINGS \
94 _Pragma("warning(push)") \
95 _Pragma("warning(disable : 4774)") // 'var' has different type in 'file1' and 'file2': 'type1' and 'type2'
96# define SPARROW_CONTRACTS_RESTORE_WARNINGS \
97 _Pragma("warning(pop)")
98#else
99# define SPARROW_CONTRACTS_IGNORE_WARNINGS
100# define SPARROW_CONTRACTS_RESTORE_WARNINGS
101#endif
102// clang-format on
103
104#ifndef SPARROW_CONTRACTS_LOG_FAILURE
105# if defined(SPARROW_CONTRACTS_USE_STD_PRINT) && SPARROW_CONTRACTS_USE_STD_PRINT == 1
106# include <cstdio>
107# include <print>
108// clang-format off
109# define SPARROW_CONTRACTS_LOG_FAILURE(expr__, message__) \
110 ::std::print(stderr, "Assertion Failed ({}:{}): {} - ({} is wrong)\n", __FILE__, __LINE__, message__, #expr__)
111// clang-format on
112# elif defined(SPARROW_CONTRACTS_USE_STD_FORMAT) && SPARROW_CONTRACTS_USE_STD_FORMAT == 1
113# include <cstdio>
114# include <format>
115// clang-format off
116# define SPARROW_CONTRACTS_LOG_FAILURE( expr__, message__ ) \
117 { \
118 auto msg = ::std::format("Assertion Failed ({}:{}): {} - ({} is wrong)\n", __FILE__, __LINE__, message__, #expr__); \
119 ::fprintf(stderr, "%s", msg.c_str()); \
120 }
121// clang-format on
122# else
123# include <cstdio>
124# include <cstdlib>
125// clang-format off
126# define SPARROW_CONTRACTS_LOG_FAILURE(expr__, message__) \
127 ::fprintf(stderr, "Assertion Failed (%s:%i): %s - (%s is wrong)\n", __FILE__, __LINE__, message__, #expr__);
128// clang-format on
129# endif
130#endif
131
132#ifndef SPARROW_CONTRACTS_ABORT
133# if SPARROW_CONTRACTS_THROW_ON_FAILURE == 1
134# if defined(SPARROW_CONTRACTS_USE_STD_FORMAT) && SPARROW_CONTRACTS_USE_STD_FORMAT == 1
135# define SPARROW_CONTRACTS_ABORT(expr__, message__) \
136 throw ::sparrow::contract_assertion_error( \
137 ::std::format("Assertion Failed ({}:{}): {} - ({} is wrong)", __FILE__, __LINE__, message__, #expr__) \
138 )
139# else
140# define SPARROW_CONTRACTS_ABORT(expr__, message__) \
141 throw ::sparrow::contract_assertion_error( \
142 ::std::string("Assertion Failed (") + __FILE__ + ":" + ::std::to_string(__LINE__) \
143 + "): " + message__ + " - (" + #expr__ + " is wrong)" \
144 )
145# endif
146# else
147# define SPARROW_CONTRACTS_ABORT(expr__, message__) std::abort()
148# endif
149#endif
150
151// User specifies to just continue instead of abort on failure.
152#if defined(SPARROW_CONTRACTS_CONTINUE_ON_FAILURE) and SPARROW_CONTRACTS_CONTINUE_ON_FAILURE == 1
153# undef SPARROW_CONTRACTS_ABORT
154# if SPARROW_CONTRACTS_THROW_ON_FAILURE == 1
155# define SPARROW_CONTRACTS_ABORT(expr__, message__)
156# else
157# define SPARROW_CONTRACTS_ABORT
158# endif
159#endif
160
161#ifndef SPARROW_CONTRACTS_DEBUGBREAK
162# ifdef _WIN32
163# define SPARROW_CONTRACTS_DEBUGBREAK() __debugbreak();
164# else
165# define SPARROW_CONTRACTS_DEBUGBREAK() std::raise(SIGTRAP);
166# endif
167#endif
168
170// Default behavior:
171
172#define SPARROW_CONTRACTS_DEFAULT_CHECKS_ENABLED 1
173#define SPARROW_CONTRACTS_DEFAULT_ABORT_ON_FAILURE 1
174
175#define SPARROW_CONTRACTS_DEFAULT_ON_FAILURE(expr__, message__) \
176 SPARROW_CONTRACTS_LOG_FAILURE(expr__, message__); \
177 SPARROW_CONTRACTS_DEBUGBREAK(); \
178 SPARROW_CONTRACTS_ABORT(expr__, message__);
179
181// Apply Configuration:
182
183// If not override, use the default behavior.
184#ifndef SPARROW_CONTRACTS_CHECKS_ENABLED
185# define SPARROW_CONTRACTS_CHECKS_ENABLED SPARROW_CONTRACTS_DEFAULT_CHECKS_ENABLED
186#endif
187
188#if SPARROW_CONTRACTS_CHECKS_ENABLED == 1 // Behavior when contracts are enabled.
189
190# ifndef SPARROW_CONTRACTS_ON_FAILURE
191# define SPARROW_CONTRACTS_ON_FAILURE(expr__, message__) \
192 SPARROW_CONTRACTS_DEFAULT_ON_FAILURE(expr__, message__)
193# endif
194
195# ifndef SPARROW_ASSERT
196# define SPARROW_ASSERT(expr__, message__) \
197 if (!(expr__)) \
198 { \
199 SPARROW_CONTRACTS_ON_FAILURE(expr__, message__); \
200 }
201# endif
202
203# define SPARROW_ASSERT_TRUE(expr__) SPARROW_ASSERT(expr__, #expr__)
204# define SPARROW_ASSERT_FALSE(expr__) SPARROW_ASSERT(!(expr__), "!(" #expr__ ")")
205
206#else // Do nothing otherwise.
207# define SPARROW_CONTRACTS_ON_FAILURE(expr__)
208# define SPARROW_ASSERT(expr__, message__)
209# define SPARROW_ASSERT_TRUE(expr__)
210# define SPARROW_ASSERT_FALSE(expr__)
211#endif