Skip to content

Commit c131a29

Browse files
replaced invalidQuESTInputError with setInputErrorHandler
The benefits include: - better portability between compilers; no macro guards! - better symmetry with existing debug API (mostly setters) - enables runtime re-routing of errors (so it can be unit tested!) - resolves problem when multiple translation units both define the previous weak-symbol function (like when deprecated tests are compiled alongside v4 tests) - more standard!
1 parent 2dccccc commit c131a29

File tree

13 files changed

+206
-48
lines changed

13 files changed

+206
-48
lines changed

examples/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ add_subdirectory(matrices)
55
add_subdirectory(numbers)
66
add_subdirectory(paulis)
77
add_subdirectory(reporters)
8-
add_subdirectory(superoperators)
8+
add_subdirectory(superoperators)
9+
add_subdirectory(debug)

examples/debug/CMakeLists.txt

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# @author Tyson Jones (by duplicating files from...)
2+
# @author Oliver Brown
3+
# @author Erich Essmann
4+
5+
6+
# C example
7+
8+
add_executable(debug_c_errorhandling
9+
errorhandling.c
10+
)
11+
12+
target_link_libraries(debug_c_errorhandling PUBLIC QuEST)
13+
14+
install(TARGETS debug_c_errorhandling
15+
RUNTIME
16+
DESTINATION ${CMAKE_INSTALL_BINDIR}/examples/debug
17+
)
18+
set_target_properties(debug_c_errorhandling
19+
PROPERTIES
20+
INSTALL_RPATH "${CMAKE_INSTALL_FULL_LIBDIR}"
21+
OUTPUT_NAME "c_errorhandling"
22+
)
23+
24+
25+
# C++ example
26+
27+
add_executable(debug_cpp_errorhandling
28+
errorhandling.cpp
29+
)
30+
31+
target_link_libraries(debug_cpp_errorhandling PUBLIC QuEST)
32+
33+
install(TARGETS debug_cpp_errorhandling
34+
RUNTIME
35+
DESTINATION ${CMAKE_INSTALL_BINDIR}/examples/debug
36+
)
37+
set_target_properties(debug_cpp_errorhandling
38+
PROPERTIES
39+
INSTALL_RPATH "${CMAKE_INSTALL_FULL_LIBDIR}"
40+
OUTPUT_NAME "cpp_errorhandling"
41+
)

examples/debug/errorhandling.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/** @file
2+
* Examples of setting the invalid error input handler in C11,
3+
* overriding the default behaviour of immediately exiting.
4+
*
5+
* @author Tyson Jones
6+
*/
7+
8+
#include "quest.h"
9+
#include <stdio.h>
10+
#include <stdlib.h>
11+
12+
13+
void myErrorHandler(const char* errFunc, const char* errMsg) {
14+
printf("Ruh-roh, Raggy! Function '%s' has reported '%s'.\n", errFunc, errMsg);
15+
printf("We will now be very good children and exit immediately!\n");
16+
exit(0);
17+
}
18+
19+
20+
int main() {
21+
initQuESTEnv();
22+
setInputErrorHandler(myErrorHandler);
23+
24+
Qureg qureg = createQureg(-123);
25+
26+
finalizeQuESTEnv();
27+
}

examples/debug/errorhandling.cpp

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/** @file
2+
* Examples of setting the invalid error input handler in C++14,
3+
* overriding the default behaviour of immediately exiting.
4+
*
5+
* @author Tyson Jones
6+
*/
7+
8+
#include "quest.h"
9+
#include <iostream>
10+
#include <stdexcept>
11+
#include <stdlib.h>
12+
13+
14+
void myErrorHandlerA(const char* errFunc, const char* errMsg) {
15+
16+
std::string func(errFunc);
17+
std::string msg(errMsg);
18+
19+
std::cout
20+
<< "an error?? in the '" << errFunc << "' function?? with message '" << msg << "'! "
21+
<< "how queer!! ive never seen such a thing - i must throw an exception post-haste!!!"
22+
<< std::endl
23+
<< std::endl;
24+
25+
// exception forces control-flow out of QuEST env, safely back to user.
26+
// without this, control-flow would return to the QuEST backend and likely
27+
// cause internal integrity checks to fail, or segmentation faults
28+
throw std::runtime_error(std::string(errFunc) + ": " + std::string(errMsg));
29+
}
30+
31+
32+
void myErrorHandlerB(const char* errFunc, const char* errMsg) {
33+
34+
std::string func(errFunc);
35+
std::string msg(errMsg);
36+
37+
std::cout
38+
<< "Function '" << func << "' threw '" << msg << "'."
39+
<< std::endl
40+
<< "We will now exit completely. Good day!"
41+
<< std::endl
42+
<< std::endl;
43+
44+
exit(0);
45+
}
46+
47+
48+
int main() {
49+
initQuESTEnv();
50+
51+
setInputErrorHandler(myErrorHandlerA);
52+
53+
try {
54+
Qureg qureg = createQureg(-123);
55+
} catch (std::runtime_error& e) {
56+
std::cout
57+
<< "Error caught! Function aborted, but execution continues."
58+
<< std::endl
59+
<< std::endl;
60+
}
61+
62+
setInputErrorHandler(myErrorHandlerB);
63+
initQuESTEnv(); // illegal to recall
64+
65+
std::cout << "this will never be reached, because myErrorHandlerB exits!" << std::endl;
66+
67+
finalizeQuESTEnv();
68+
}

quest/include/debug.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ int getNumSeeds();
4444
*/
4545

4646
/// @notdoced
47-
void invalidQuESTInputError(const char* msg, const char* func);
47+
void setInputErrorHandler(void (*callback)(const char* func, const char* msg));
4848

4949
/// @notdoced
5050
void setValidationOn();

quest/src/api/debug.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@ void getSeeds(unsigned* seeds) {
6464
* VALIDATION
6565
*/
6666

67+
void setInputErrorHandler(void (*callback)(const char*, const char*)) {
68+
validate_envIsInit(__func__);
69+
70+
validateconfig_setErrorHandler(callback);
71+
}
6772

6873
void setValidationOn() {
6974
validate_envIsInit(__func__);

quest/src/core/validation.cpp

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1044,9 +1044,8 @@ namespace report {
10441044
/*
10451045
* INVALID INPUT RESPONSE BEHAVIOUR
10461046
*/
1047-
extern "C" {
1048-
// default C/C++ compatible error response is to simply exit in fail state
1049-
void default_invalidQuESTInputError(const char* msg, const char* func) {
1047+
1048+
void default_inputErrorHandler(const char* func, const char* msg) {
10501049

10511050
// safe to call even before MPI has been setup, and ignores user-set trailing newlines
10521051
print(string("")
@@ -1062,26 +1061,16 @@ void default_invalidQuESTInputError(const char* msg, const char* func) {
10621061
if (comm_isInit())
10631062
comm_end();
10641063

1064+
// simply exit, interrupting any other process (potentially leaking)
10651065
exit(EXIT_FAILURE);
10661066
}
10671067

1068-
// enable default error response to be user-overriden as a weak symbol (even in C, and on Windows)
1069-
1070-
// Always declare invalidQuESTInputError so the compiler sees it:
1071-
void invalidQuESTInputError(const char* msg, const char* func);
1068+
void (*global_inputErrorHandler)(const char*, const char*) = default_inputErrorHandler;
10721069

1073-
#ifndef _WIN32
1074-
#pragma weak invalidQuESTInputError
1075-
void invalidQuESTInputError(const char* msg, const char* func) {
1076-
default_invalidQuESTInputError(msg, func);
1077-
}
1078-
#elif defined(_WIN64)
1079-
#pragma comment(linker, "/alternatename:invalidQuESTInputError=default_invalidQuESTInputError")
1080-
#else
1081-
#pragma comment(linker, "/alternatename:_invalidQuESTInputError=_default_invalidQuESTInputError")
1082-
#endif
1070+
void validateconfig_setErrorHandler(void (*callback)(const char*, const char*)) {
10831071

1084-
} // end C++ de-mangler
1072+
global_inputErrorHandler = callback;
1073+
}
10851074

10861075

10871076

@@ -1217,8 +1206,9 @@ void assertThat(bool valid, string msg, const char* func) {
12171206
// uniform between nodes (assuming user's do not hack in rank-specific
12181207
// arguments to the API!)
12191208

1209+
// invoke the potentially user-overriden error function
12201210
if (!valid)
1221-
invalidQuESTInputError(msg.c_str(), func);
1211+
global_inputErrorHandler(func, msg.c_str());
12221212
}
12231213
void assertThat(bool valid, string msg, tokenSubs vars, const char* func) {
12241214

quest/src/core/validation.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,14 @@ const int validate_STRUCT_PROPERTY_UNKNOWN_FLAG = -1;
3131

3232

3333

34+
/*
35+
* VALIDATION ERROR HANDLER
36+
*/
37+
38+
void validateconfig_setErrorHandler(void (*callback)(const char* func, const char* msg));
39+
40+
41+
3442
/*
3543
* VALIDATION TOGGLE
3644
*/

tests/deprecated/test_main.cpp

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,16 @@
2727
#include <stdexcept>
2828

2929

30-
/* Redefinition of QuEST_validation's invalidQuESTInputError function, called when a
31-
* user passes an incorrect parameter (e.g. a negative qubit index). This is
32-
* redefined here to, in lieu of printing and exiting, throw a C++ exception
33-
* which can be caught (and hence unit tested for) by Catch2
30+
31+
/*
32+
* recast QuEST errors into exceptions which Catch2 can intercept
3433
*/
35-
extern "C" void invalidQuESTInputError(const char* errMsg, const char* errFunc) {
36-
37-
throw std::runtime_error(errMsg);
38-
}
34+
35+
/// @private
36+
extern "C" void validationErrorHandler(const char* errFunc, const char* errMsg) {
37+
38+
throw std::runtime_error(std::string(errFunc) + ": " + std::string(errMsg));
39+
}
3940

4041

4142
/** Explicit declaration of main to create (destroy) the QuESTEnv before (after)
@@ -44,6 +45,7 @@
4445
int main(int argc, char* argv[]) {
4546

4647
initQuESTEnv();
48+
setInputErrorHandler(validationErrorHandler);
4749
setRandomTestStateSeeds();
4850

4951
int result = Catch::Session().run( argc, argv );

tests/main.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,11 @@
5656

5757

5858
/*
59-
* recast QuEST errors into exceptions which Catch can intercept
59+
* recast QuEST errors into exceptions which Catch2 can intercept
6060
*/
6161

6262
/// @private
63-
extern "C" void invalidQuESTInputError(const char* errMsg, const char* errFunc) {
63+
extern "C" void validationErrorHandler(const char* errFunc, const char* errMsg) {
6464

6565
throw std::runtime_error(std::string(errFunc) + ": " + std::string(errMsg));
6666
}
@@ -117,6 +117,7 @@ int main(int argc, char* argv[]) {
117117
// prepare QuEST before anything else, since many
118118
// testing utility functions repurpose QuEST ones
119119
initQuESTEnv();
120+
setInputErrorHandler(validationErrorHandler);
120121

121122
// ensure RNG consensus among all nodes
122123
setRandomTestStateSeeds();

0 commit comments

Comments
 (0)