Let's say I have a template function that takes an integer and a const reference to an instance of type T. Now depending on the integer, only some T's are acceptible, otherwise an exception is thrown at runtime.
If all uses of this function would use constant integers, it would be possible to make the int a template parameter and use a static assertion to check if it is acceptable. So instead of func(1,c) one would use func<1>(c) and would gain compile-time type checking. Is there any way to write func(1,c) and still keep the compile-time check, while also being able to write func(i,c) and use a dynamic assertion? The goal is to make it transparent to the developer. It would simply be great to add this safety without bothering the developers about things like compile-time constants. They'd probably only remember that func(1,c) always works and use that, avoiding the check.
How can I define a function with a static assertion whenever possible and a dynamic assertion otherwise?
The following code shows the solution for GCC by Ivan Shcherbakov:
#include <iostream>
#include <cassert>
template<typename T>
void __attribute__((always_inline)) func(const int& i, const T& t);
void compile_time_error_() __attribute__((__error__ ("assertion failed")));
template<>
void __attribute__((always_inline))
func(const int& i, const float& t)
{
do {
if (i != 0) {
if (__builtin_constant_p(i)) compile_time_error_();
std::cerr << "assertion xzy failed" << std::endl;
exit(1);
}
} while (0);
func_impl<float>(i,t);
}
This will only allow the combination of i=0 and T=float. For other combinations a good way would be creating a Macro that produces the code of template<> func(const int& i, const T& t) with T and i != 0 replaced.
Well, if you're using GCC, you can use a dirty hack, but it will only work when function inlining is enabled (-O1 or more):
void my_error() __attribute__((__error__ ("Your message here")));
template <typename T1, typename T2> struct compare_types
{
enum {Equals = 0};
};
template <typename T1> struct compare_types<T1,T1>
{
enum {Equals = 1};
};
template <typename Type> __attribute__((always_inline)) void func(int a, Type &x)
{
if (__builtin_constant_p(a))
{
if (a == 1 && compare_types<Type,char>::Equals)
my_error();
}
}
In this case when a == 1 and Type is char, you'll get an error. Here's an example that will trigger it:
int main()
{
char x;
func(1, x);
return 0;
}
Note that this example heavily relies on the gcc-specific __builtin_constant_p() function and won't work with other compilers!
Let me paraphrase question to be more accurate in my answer:
Can runtime assert sometimes report errors on compile time.
Gcc can, but only on some optimization level and error message is
very uninformative. Clang itself can't (no error attribute), but do
not forget about clang analyzer.
Analyzer can report some runtime errors like dereferencing null pointer.
So here is an idea and simple test of 'smart' runtime assert:
#include <cstdlib> // std::abort
#if !defined(__clang__) && defined(__GNUC__)
// clang emulates gcc
# define GCC_COMPILER 1
#else
# define GCC_COMPILER 0
#endif
#if GCC_COMPILER
void assertion_failed_message() __attribute__((__error__("assertion failed")));
#endif
inline void smart_assert(bool condition) {
#if GCC_COMPILER
// gcc is very 'sensitive', it must be first code lines in this function
if (__builtin_constant_p(condition) && !condition) {
assertion_failed_message();
}
#endif
if (condition) {
// Ok
return;
}
#if defined(__clang_analyzer__)
enum {
ASSERTION_FAILED = 0xdeadfull
};
int *a = nullptr;
*a = ASSERTION_FAILED;
#endif
// Implement some standart error, like abort
std::abort();
}
void test_condition_2(bool condition) {
smart_assert(condition);
}
void test_condition_1(bool condition) {
test_condition_2(condition);
}
void test_condition_0(bool condition) {
test_condition_1(condition);
}
int main() {
test_condition_0(0==1);
return EXIT_SUCCESS;
}
Gcc report error at O2 optimization level, it's good. But report message
is in main function, and don't leave any information
about test_condition_{0,1,2}.
Clang analyzer report error and if you use Xcode, you can see all path
from main to smart_assert:
P.S. clang analyzer is not perfect, so if you try test_condition_0(argc),
no error will be reported (truely runtime check), but if you try
test_condition_0(argc==1), false positive will be reported.
Related
I am building my program by using the latest Emscripten compiler.
It is based on Clang version 14. Actually it is a small test program which is the following:
#include <iostream>
struct Test {
template<typename T>
static inline void Dump(const T& value) {
std::cout << "[generic] = '" << value << "'\n";
}
template<>
static inline void Dump<std::string>(const std::string& value) {
std::cout << "[std::string] = '" << value << "'\n";
}
};
int main() {
std::string text = "hello";
Test::Dump(text);
return 0;
}
When I build it by Emscripten compiler I got the warning:
D:\em_test>emcc a.cpp
a.cpp:10:24: warning: explicit specialization cannot have a storage class
static inline void Dump<std::string>(const std::string& value) {
~~~~~~~ ^
1 warning generated.
If I just remove static keyword from void Dump<std::string> line
then there will be no warning. However, this code will cause compilation error in Visual Studio:
D:\em_test\a.cpp(17,11): error C2352: 'Test::Dump': illegal call of non-static member function
But this error is expected and clear.
I would like to write a cross-platform program.
So, I think I should simple disable this warning in Emscripten.
However, I can not find any Emscripten (which is based on clang version 14)
command line option for that!
And I am asking advice for that.
Actually I tried to use -Wno-static-inline-explicit-instantiation command line option but it did not help:
D:\em_test>emcc -Wno-static-inline-explicit-instantiation a.cpp
a.cpp:10:24: warning: explicit specialization cannot have a storage class
static inline void Dump<std::string>(const std::string& value) {
~~~~~~~ ^
1 warning generated.
However, I see in Clang version 13 user manual description about -Wstatic-inline-explicit-instantiation option but it is about a slightly another warning text.
Also it seems that Clang version 14 is not fully released, so, there is no public Clang version 14 user manual.
I can not find any Emscripten or Clang command line option to disable the above warning.
Could somebody help me?
Explicit specialization of (both static and non-static) function templates cannot be put into class definitions.
Just put it into the enclosing namespace(i.e somewhere after the class):
#include <iostream>
struct Test {
template <typename T>
static inline void Dump(const T& value) {
std::cout << "[generic] = '" << value << "'\n";
}
};
// Notice Test::
template <>
inline void Test::Dump<std::string>(const std::string& value) {
std::cout << "[std::string] = '" << value << "'\n";
}
int main() {
std::string text = "hello";
Test::Dump(text);
return 0;
}
inline is never necessary for in-class function definitions but it has different meaning for member variables.
inline for out-class is necessary in header files because the explicit specialization is not a template anymore.
I am trying to use std::optional but my code raise error.
I have specified #include <experimental/optional> and compiler options are -std=c++1z, -lc++experimental.
How to use std::experimental::optional?
The following is code:
#include <experimental/optional>
#include <iostream>
std::experimental::optional<int> my_div(int x, int y) {
if (y != 0) {
int b = x / y;
return {b};
}
else {
return {};
}
}
int main() {
auto res = my_div(6, 2);
if (res) {
int p = res.value();
std::cout << p << std::endl;
}
}
error message:
optional.cpp:17:21: error: call to unavailable member function 'value':
int p = res.value();
~~~~^~~~~
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/experimental/optional:525:17: note: candidate function has been explicitly made unavailable
value_type& value()
^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/experimental/optional:517:33: note: candidate function has been explicitly made unavailable
constexpr value_type const& value() const
^
1 error generated.
OS: macOS 10.12.5
Compiler version:
Apple LLVM version 8.1.0 (clang-802.0.42)
Target: x86_64-apple-darwin16.6.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
Okay, after you posted your error, I could look into that (but you could have done exactly the same).
IN SHORT
This is a problem/bug with optional as provided by Apple on OSX, but there is an easy workaround.
WHAT'S GOING ON
The file /Library/Developer/CommandLineTools/usr/include/c++/v1/experimental/optional declares the offending function optional::value as
template <class _Tp>
class optional
: private __optional_storage<_Tp>
{
/* ... */
_LIBCPP_INLINE_VISIBILITY _LIBCPP_AVAILABILITY_BAD_OPTIONAL_ACCESS
constexpr value_type const& value() const
{
if (!this->__engaged_)
throw bad_optional_access();
return this->__val_;
}
_LIBCPP_INLINE_VISIBILITY _LIBCPP_AVAILABILITY_BAD_OPTIONAL_ACCESS
value_type& value()
{
if (!this->__engaged_)
throw bad_optional_access();
return this->__val_;
}
/* ... */
};
Running the preprocessor only (compiler option -E) reveals that the macros expand to
#define _LIBCPP_INLINE_VISIBILITY \
__attribute__ ((__visibility__("hidden"), __always_inline__))
#define _LIBCPP_AVAILABILITY_BAD_OPTIONAL_ACCESS \
__attribute__((unavailable))
In particular, the macro _LIBCPP_AVAILABILITY_BAD_OPTIONAL_ACCESS is #defined in file /Library/Developer/CommandLineTools/usr/include/c++/v1/__config as
// Define availability macros.
#if defined(_LIBCPP_USE_AVAILABILITY_APPLE)
// ...
#define _LIBCPP_AVAILABILITY_BAD_OPTIONAL_ACCESS __attribute__((unavailable))
// ...
#else
// ...
#define _LIBCPP_AVAILABILITY_BAD_OPTIONAL_ACCESS
// ...
#endif
Thus, this is a Apple specific change from LLVM's libc++ API. As the name of the macro implies, the reason is that Apple does not make
class bad_optional_access
: public std::logic_error
{
public:
bad_optional_access() : std::logic_error("Bad optional Access") {}
virtual ~bad_optional_access() noexcept;
};
available and hence cannot implement functionality (optional::value) that depends on it. Why bad_optional_access is not provided (thereby breaking the standard) is unclear, but it may have to do with the fact that a library (dylib) must be altered to contain bad_optional_access::~bad_optional_access().
HOW TO WORK AROUND
simply use optional::operator* instead
int p = *res;
The only real difference is that no access check is done. If you need that, do it yourself
template<typename T>
T& get_value(std::experimental::optional<T> &opt)
{
if(!opt.has_value())
throw std::logic_error("bad optional access");
return *opt;
}
template<typename T>
T const& get_value(std::experimental::optional<T>const &opt)
{
if(!opt.has_value())
throw std::logic_error("bad optional access");
return *opt;
}
I have a template class like below. It is meant to be instantiated with either a 32-bit word or a 64-bit word.
foo.h
template <typename WORD>
class Foo
{
public:
...
void Init();
...
private:
WORD* m_ptr;
};
foo.cpp
template <typename WORD>
void Foo<WORD>::Init()
{
#if (sizeof(WORD) == 8)
// Do something
#elif (sizeof(WORD) == 4)
// Do something else
#endif
}
Compiling it results in error: missing binary operator before token "(" under GCC; and token is not a valid binary operator in a preprocessor subexpression under Clang.
I don't want to provide separate specializations because this is the exact sort of code templates were intended for. I also believe it will break some things we do with base class pointers and references (in derived classes).
How can I access the size of the template parameter WORD and use it to select code paths?
The following will work fine, but it generates warnings I am trying to squelch. For example, it generates a result_independent_of_operands under Coverity:
WORD unused;
if (sizeof(unused))
{
...
}
I also get that the preprocessor runs before the compiler. I don't have a misunderstanding about that (e.g., sizeof() is not executed by preprocessor and friends).
But that source code is saved an written in stone long before the preprocessor even runs, and the size of uint32_t and uint64_t never changes, so all this information is available at every stage of the compilation process. I just don't know how to access it.
The preprocessor knows absolutely nothing about templates, template parameters, or types. That's a dead end.
Perhaps tag dispatching will make your analysis tool shut up.
template <typename WORD>
class Foo
{
public:
void Init();
private:
WORD* m_ptr;
template<std::size_t> struct size_tag {};
void Init_impl(size_tag<8>) { /* Do something */ }
void Init_impl(size_tag<4>) { /* Do something else */ }
};
template <typename WORD>
void Foo<WORD>::Init()
{
Init_impl(size_tag<sizeof(WORD)>());
}
// in someFile.h or someFile.cpp
TEST()
{
"example test", []
{
EXPECT(0 == 1);
}
}
TEST_END()
// in main.cpp
int main() { ssvu::Test::runAllTests(); }
#define TEST() static RunOnCtor UNIQUENAME(__LINE__) { []{ getStaticTests().push_back({
#define TEST_END() });}};
struct RunOnCtor { RunOnCtor(std::function<void()> f) { f(); } };
I've created some unit testing macros that work both in header (useful for my header-only libraries) and source files.
TEST() creates a static instance of RunOnCtor, which, on construction, executes a lambda which inserts the test lambda into a std::vector of tests. runAllTests() runs every test in that vector.
I have created a DISABLE_TEST define that simply puts return; at the beginning of the RunOnCtor lambda, so that no tests are added to the internal vector. However, I would like to prevent unnecessary construction of static objects when tests are disabled.
Is there a way to completely ignore everything between TEST() and TEST_END()? Something like commenting it out. Or would emptying out RunOnCtor (empty struct) make the compiler avoid static constructions?
I think this should work:
#define TEST() struct UNIQUENAME(__LINE__) { void f() {
#define TEST_END() } };
The following is based on #Angew 's answer but uses standard preprocessor definitions. The class then defines a function within the class declaration, which in C++ forces it to be inline. Because the class is never instantiated and the function is never called, no code is ever generated.
#define CAT(a, ...) PCAT(a, __VA_ARGS__)
#define PCAT(a, ...) a ## __VA_ARGS__
#define TEST() class CAT(Obfuscate_, __LINE__) { void f() {
#define TEST_END() } };
int c;
TEST()
int a = 7;
int b = a * 17;
c = b + 4;
return;
TEST_END()
Here's Godbolt's handy-dandy compiler explorer to prove that GCC, CLANG, and ICC produce no code for the above: https://godbolt.org/g/BXKDNF
Here’s an example setup… a macro or a template CHECKEXPR_RETURNVAL(EXPR,VAL) that checks that EXPR is TRUE while returning VAL.
This is useful in a variety of places -- like in this highly simplified example:
#define ISPOW2(VAL) ((0!=VAL)&&(0==(VAL&(VAL-1))))
#define _ALIGNPOW2(VAL,ALIGN) ((VAL+(ALIGN-1))&(~(ALIGN-1)))
#define ALIGNPOW2(VAL,ALIGN) CHECKEXPR_RETURNVAL( \
ISPOW2(ALIGN) , _ALIGNPOW2(VAL,ALIGN) )
So, the difficulty is this: I want to do compile time checks if possible, and if the value is not a constant that is determinate at compile time, then do a runtime check.
Basically, the idea is to catch bad parameters as soon as possible; if you can catch a bad parameter at compile time that's better than finding out at run time. Also, the compile time version is required for constant initializers.
Here are my two (failed) attempts to make single version work in multiple places (as a constant array size, as an enum initializer, and in a function with variables). Unfortunately, they either work for the compile time only (constant initializer) or the runtime only -- I would like to figure out a version that will work for both.
// CHECKEXPR_RETURNVAL - version "A"
#define CTCHECK_EXPR(EXP)(CTCheckBool<EXP>::ExistsZeroIfTrue)
template <bool bExpression> struct CTCheckBool {};
template <> struct CTCheckBool<true> {enum{ExistsZeroIfTrue=0};};
// Note: Plus ("+") is used rather than comma operator because
// the comma operator can not be used for constant initializers
#define CHECKEXPR_RETURNVAL_A(EXP,VAL) (CTCHECK_EXPR(EXP) + (VAL))
// Test Out version "A" -- works only for Compile Time Constants
#define ALIGNPOW2_A(VAL,ALIGN) CHECKEXPR_RETURNVAL_A( \
ISPOW2(ALIGN) , _ALIGNPOW2(VAL,ALIGN) )
char AlignedVar_A[ALIGNPOW2_A(2,8)];
enum { AlignedVal_A = ALIGNPOW2_A(57,16) };
int TestAlignPow2_A(int val, int align)
{return(ALIGNPOW2_A(val,align));} // Compile Error
// CHECKEXPR_RETURNVAL - version "B"
template<typename T> T CHECKEXPR_RETURNVAL_B(bool bExpr,T val)
{ ASSERT(bExpr); return(val); }
// Test Out version "B" -- works only for Runtime Computed Values
#define ALIGNPOW2_B(VAL,ALIGN) CHECKEXPR_RETURNVAL_B( \
ISPOW2(ALIGN) , _ALIGNPOW2(VAL,ALIGN) )
char AlignedVar_B[ALIGNPOW2_B(2,8)]; // Compile Error
enum { AlignedVal_B = ALIGNPOW2_B(57,16) }; // Compile Error
int TestAlignPow2_B(int val, int align)
{return(ALIGNPOW2_B(val,align));}
Unfortunately, neither version works for all three cases. Is there a code structure that will work for all the cases ?
It seems like you would really make use of c++0x constexpr functions...
Well... Not a complete answer, but I think you can derive what you want from this:
#include <stdio.h>
template <int I> struct S{
static void doIt(){
fprintf(stderr, "wow\n");
}
};
template<> void S<0>::doIt(){
fprintf(stderr, "oops\n");
}
#define EXPR(p) S<(int)((bool)(p))>::doIt()
int main(int argc, char** argv){
EXPR((5+2)==7);
EXPR((5+2)==8);
const int a = 58;
EXPR(a==58);
EXPR(58);
return 0;
}
You can get a compiler error based on expression:
#include <stdio.h>
template <int I> struct S{
static void doIt(){
ssdfkjehiu //deliberately invalid code
fprintf(stderr, "oops\n");
}
};
template<> void S<1>::doIt(){
fprintf(stderr, "wow\n");
}
#define EXPR(p) S<(int)((bool)(p))>::doIt()
int main(int argc, char** argv){
EXPR((5+2)==7);
EXPR((5+2)==8);//uncomment it to make code compile
const int a = 58;
EXPR(a==58);
EXPR(58);
return 0;
}
But the line that causes error will be in a middle of a lengthy template error message. Example:
1.cpp(6) : error C2065: 'asdlfkjhasd' : undeclared identifier
1.cpp(4) : while compiling class template member function 'void S<I>::doIt(void)'
with
[
I=0
]
1.cpp(19) : see reference to class template instantiation 'S<I>' being compiled
with
[
I=0
]
1.cpp(6) : error C2146: syntax error : missing ';' before identifier 'fprintf'
As you see, error is caused by line 19, which is mentioned in the middle of the message. This is a bit inconvenient.
I cannot guarantee that both examples doesn't rely on some undefined C++ behavior.
Also, I think that the next code maintainer may not be happy about this...
P.S. I think you should also take a look at boost. If I remember correctly, it had quite a lot of "magic preprocessor macro" (loops, for example), so it is possible that it implemented something similar.
--edit--
Okay, what about this one?:
#include <stdio.h>
#include <string>
template <typename T> void a(T &i){
fprintf(stderr, "variable\n");
}
void a(const char* const i){
fprintf(stderr, "constant\n");
}
void a(bool i){
fprintf(stderr, "constant\n");
}
int main(int argc, char** argv){
int i;
float f;
std::string s;
const char* s1 = "b";
a(3);
a(3+2);
a(1.0f);
a('a');
a("b");
a(i);
a(f);
a(s);
a(s1);
return 0;
}