Any ideas on how to turn this into a warning? - c++

I'm using this is_enum function to check if a variable is an enum or not.
(See error below)
#include <boost/type_traits/is_enum.hpp>
#include <boost/static_assert.hpp>
template<typename T>
void is_enum(T)
{
BOOST_STATIC_ASSERT(boost::is_enum<T>::value == true);
}
int main()
{
char c = 'a';
is_enum(c);
return 0;
}
This gives me the following error:
-*- mode: compilation; default-directory: "/home/epronk/enums/" -*-
Compilation started at Thu Nov 10 21:20:05
g++ -I /home/epronk/src/boost_1_47_0/ q.cpp
q.cpp: In function ‘void is_enum(T) [with T = char]’:
q.cpp:13: instantiated from here
q.cpp:7: error: invalid application of ‘sizeof’ to incomplete type ‘boost::STATIC_ASSERTION_FAILURE<false>’
q.cpp:7: error: invalid application of ‘sizeof’ to incomplete type ‘boost::STATIC_ASSERTION_FAILURE<false>’
Compilation exited abnormally with code 1 at Thu Nov 10 21:20:05
(not sure why g++ (Debian 4.4.5-8) 4.4.5 give me the same error twice)
Is it possible to change this function so it becomes a warning?
For a char you can try to assign 256 to it which results in a overflow error.
edited
Some context: I want to find switch statements like this one.
#define switch(arg) \
is_enums(arg); \
switch(arg)
int main()
{
char c = Red;
switch(c)
{
case Banana: // No warning
break;
case Red:
break;
case Green:
break;
case Blue:
break;
}
return 0;
}

EDIT: Try BOOST_STATIC_WARNING http://www.boost.org/doc/libs/1_46_1/libs/serialization/doc/static_warning.html; the code below is my hand-hacked version of doing something similar.
Something like this:
#include <boost/type_traits/is_enum.hpp>
#include <boost/utility.hpp>
#include <boost/static_assert.hpp>
enum AB { A, B };
template<typename T>
typename boost::enable_if_c< boost::is_enum<T>::value,
void >::type is_enum(T) {
}
template<typename T>
typename boost::enable_if_c< !boost::is_enum<T>::value,
void >::type is_enum(T) {
int NOT_AN_ENUMERATION = 1;
}
int main()
{
char c = 'a';
is_enum(c);
is_enum(A);
is_enum(B);
return 0;
}
Will issue a warning about the unused variable if you get your compiler in the right state. With gcc and '-Wall', I get this sort of thing:
thing.cpp: In function 'typename boost::enable_if_c<(! boost::is_enum::value), void>::type is_enum(T) [with T = char]':
thing.cpp:21: instantiated from here
thing.cpp:15: warning: unused variable 'NOT_AN_ENUMERATION'

The error is intentional, since you call a static assert, which means exactly "please trigger a compile-time error if the condition is false".
Your function is strangely named, though: It's not a conditional check whether the variable is an enum, but rather an assertion that it is. You should call it assert_that_var_is_enum_or_die() or something meaningful like that.
A conditional should probably just be:
inline bool is_enum(T) { return boost::is_enum<T>::value; }

If you are using boost, you can use BOOST_STATIC_WARNING defined in header <boost/serialization/static_warning.hpp>. If that does not work on your compiler you can use BOOST_SERIALIZATION_BSW defined in same header. Example code looks like BOOST_SERIALIZATION_BSW(std::is_enum<T>::value,1); where the second parameter is an unique integer. The implementation is conceptually same as BOOST_STATIC_ASSERT except that it uses compiler specific warning such as "negative integer to unsigned conversion" for generation purpose.

Related

Why does gcc8.3 appear to attempt to compile an unused template function?

Consider (the name of the file is hello.cpp) this code; the idea is to engineer a safe casting of numeric types without loss or overflow. (I'm porting some code from MSVC to g++).
#include <cstdint>
#include <iostream>
template<
typename T/*the desired type*/,
typename/*the source type*/ Y
> T integral_cast(const Y& y)
{
static_assert(false, "undefined integral cast");
}
// Specialisation to convert std::uint32_t to double
template<>
inline double integral_cast(const std::uint32_t& y)
{
double ret = static_cast<double>(y);
return ret;
}
int main()
{
std::uint32_t a = 20;
double f = integral_cast<double>(a); // uses the specialisation
std::cout << f;
}
When I compile with gcc 8.3 by typing g++ -o hello hello.cpp I get the error error: static assertion failed: undefined integral cast.
This means that g++ is always compiling the unused template code.
Note that MSVC compiles this (which is nice since it allows me to spot any integral cast specialisations that I haven't considered).
Clearly I'm missing something. But what?
GCC isn't really "instantiating" or "compiling" the base function template. If it was, you would have two compiled functions with the same name and parameter list. As #Raymond Chen pointed out in the comments, GCC is permitted, but not required, to raise an error for templates that don't have any valid instantiation.
For example:
template<
typename T/*the desired type*/,
typename/*the source type*/ Y
> T integral_cast(const Y& y)
{
static_assert(sizeof(Y) == 1);
};
Won't raise an error in the example you give (because it has a valid instantiation and is not instantiated).
I suspect GCC just needs to substitute the types into the base template for overload resolution, so it really just needs the declaration, not the definition.
You can get the behavior you want by using a deleted definition:
template<
typename T/*the desired type*/,
typename/*the source type*/ Y
> T integral_cast(const Y& y) = delete;

Array bound set by a function of a generic

I want to set the array length to be the minimum of a constant and a generic like this:
template <int foo> struct Bar{
void my_func( int const (&my_array)[std::min(5, foo)] ) { /*...*/ }
};
This code compiles with clang++ but not g++ and I need my code to work with both. The error g++ gives is: error: array bound is not an integer constant before ']' token. How I can set the length of this array to be the minimum of foo and 5?
When I use clang++ I run into the problem that I can't get anything to bind to my_array. I want to run something like:
int main() {
static const int var[5] = {0,1,2,3,4};
Bar<5> bar;
bar.my_func(var);
}
But when I try to compile this code in clang++ I get: error: reference to type 'const int [*]' could not bind to an lvalue of type 'const int [5]'.
If I get rid of the std::min() stuff and replace it with foo the code compiles and runs fine.
Notes:
To get this code to compile you'll need to #include <algorithm> or similar to access std::min.
I don't think that this being part of a template should matter but when I try similar things with non-template function such as:
const int const_five = 5;
void new_func( int const (&my_array)[std::min(5,const_five)] ) { /*...*/ }
g++ says: error: variable or field 'new_func' declared void and clang++ says candidate function not viable: no known conversion from 'const int [5]' to 'const int [std::min(5, const_five)]' for 1st argument which both look like similar problems.
For int const (&my_array)[std::min(5, foo)] to compile, you need a version of std::min which is constexpr. It is since C++14.
Check the default value for -std of gcc and clang you use (its version-dependant). Ultimately, compile with -std=c++14.
Provided by StoryTeller, a nice working MCVE.
Keep it simple:
[foo < 5 ? foo : 5]

is decltype required in trailing return using value of argument type

In trying to understand c++17 compliant code, i am confused by the following code in
which the function uses the value from integral_constant type argument in the
trailing return type. (please feel free to correct my terminolgy etc, trying to learn)
Two simple versions are illustrated below , with and without a decltype on the
argument in the trailing return.
Using Compiler Explorer https://godbolt.org/z/vqmzhu
The first (bool_from1) compiles ok on
MSVC 15.8; /std:c++17, /O2, /permissive-
and clang 8.7.0.0 and gcc 8.2; -std=c++17, -O2, -pedantic
with correct assembler output
The second (bool_from2) errors out on gcc,
It also shows Intellisense errors in VS but compiles without error.
I could not find anything in cppreference or standard draft, etc that would indicate to me that the decltype(atype) would be required for conforming code, however...???
My question would be is the decltype required.
Are my compiler flags correct for c++17 conformance checking.
CODE:
#include <utility>
namespace ns {
volatile bool vb;
template<bool B> struct bool_ : std::bool_constant<B> {};
// using decltype(btype) in trailing return compiles in all 3
template<typename Bool> constexpr auto bool_from1(Bool btype)
-> bool_<decltype(btype)::value> {
return bool_<btype.value>{};
}
void test1() {
static_assert( // simple test
bool_from1(std::true_type{}).value
);
vb = bool_from1(std::true_type{}).value; // check output
}
// without decltype in trailing return compile in VS and clang
// but errors out in gcc; and VS shows Intelisense errors but compiles
template<typename Bool>
constexpr auto bool_from2(Bool btype)
// ^ gcc 8.2 error: deduced class type 'bool_' in function return type
-> bool_<btype.value> {
// ^ gcc: invalid template-id; use of paramter outside function body before '.'
//^ VS Intellisense on btype: <error-constant>; a paramter is not allowed
return bool_<btype.value>{};
}
void test2() {
static_assert(
bool_from2(std::true_type{}).value
//^ gcc: bool_from1 was not declared in this scope
);
vb = bool_from2(std::true_type{}).value; // check output
}
}
This looks like a gcc bug and bug report: "Trailing return types" with "non-type template arguments" which could be "constant expressions" produce a parsing error seems to fit this case:
Consider the following snippet:
template <int>
struct bar {};
template <class I>
auto foo(I i) -> bar<i()> { return {}; }
int main()
{
foo([]{ return 1; }); // (0)
}
This compiles and works as intended on clang++5, but produces a
compile-time error on g++7:
prog.cc:5:25: error: template argument 1 is invalid
auto foo(I i) -> bar<i()> { return {}; }

gcc warning "-WOverflow"

please consider the following snippet of C++98 code:
#include <iostream>
#include <limits>
template <typename T> T myLower() {
if (std::numeric_limits<T>::is_integer) {
return std::numeric_limits<T>::min();
} else {
return -std::numeric_limits<T>::max();
}
}
int main(int argc, char *argv[]) {
std::cout << (int)myLower<uint8_t>() << "\n";
return 0;
}
When compiled with g++ test.cpp && ./a.out it works as expected: 0 is printed. But when compiled with any optimization (-O1 upwards, -Os as well) the -Woverflow warning is emitted for the else-branch, which should only be there for floating-point numbers:
g++ test.cpp -Wall -O1 && ./a.out
test.cpp: In instantiation of ‘T myLower() [with T = unsigned char]’:
test.cpp:16:46: required from here
test.cpp:9:16: warning: large integer implicitly truncated to unsigned type [-Woverflow]
retval = -std::numeric_limits<T>::max();
~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0
This also fails for uint16, all other types work as they should. As far as I can see the std::numeric_limits<uint8_t>::is_integer should be a compile-time constant -- why is the else-branch compiled at all? The warning is not serious or did I miss something?
Ah, this happens in gcc5 and gcc6.
Thanks!
PS: I know that there will be std::numeric_limits<>::lowest() when the future will have arrived here...
The problem here is that
if (std::numeric_limits<T>::is_integer)
Does not limit what T can be. That means the else brance is always compiled and can trigger a warning if the operation would cause an overflow even if the branch is never reached.
If you had C++17 you could use if constexpr like
template <typename T> T myLower() {
if constexpr (std::numeric_limits<T>::is_integer) {
return std::numeric_limits<T>::min();
} else {
return -std::numeric_limits<T>::max();
}
}
Which would only compile the if block or the else block depending on the constexpr condition. Before C++17 you would have to use SFINAE to constrain the function to only compile if the template resolves.

Can offsetof be used with a struct type obtained from decltype?

Can offsetof be used with a type obtained through decltype? Is either of those cases valid C++11?
struct S {
int i;
int j { offsetof(decltype(*this), i) }; // case 1
S() : i(offsetof(decltype(*this), j)) {}; // case 2
} inst1;
int main() {
struct {
int i;
int j { offsetof(decltype(*this), i) }; // case 3
} inst2;
return 0;
}
None of it compiles under Apple LLVM version 6.0 (clang-600.0.57) (based on LLVM 3.5svn), with the error
error: offsetof requires struct, union, or class type,
'decltype(*this)' (aka '<anonymous struct at ../qxjs3uu/main.cpp:4:4> &') invalid
It also seems to crash MSVC 19.00.23106.0(x86) with an internal error:
Compiled with /EHsc /nologo /W4 /c
main.cpp
main.cpp(3): error C2062: type 'S &' unexpected
[...]
main.cpp(4): fatal error C1903: unable to recover from previous error(s); stopping compilation
Internal Compiler Error in c:\tools_root\cl\bin\i386\cl.exe. You will be prompted to send an error report to Microsoft later.
Did I think of something that no test case writers thought of?
The result of dereferencing a pointer is an lvalue (and itself is an expression), thus decltype(*this) gives you type S&:
§ 7.1.6.2 [dcl.type.simple]/p4:
The type denoted by decltype(e) is defined as follows:
— [...]
— otherwise, if e is an lvalue, decltype(e) is T&, where T is the type of e;
To use it as an argument to offsetof, you'd need to remove a reference from a type obtained from the decltype() specifier:
offsetof(std::remove_reference<decltype(*this)>::type, i)
DEMO
They can be used together. Take the example:
#include <iostream>
#include <string>
#include <stddef.h>
int main()
{
struct S {
int a;
int b;
};
S instance;
std::cout << "offset: " << offsetof(decltype(instance), b) << "\n";
return 0;
}
Which prints offset: 4
I think your problem stems from using decltype(*this), which I'm not sure will do what you're expecting according to 5.1.1 of the C++11 standard.