Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[34.231.180.210] |
|
Сообщ.
#1
,
|
|
|
Собственно, сабж:
#include <iostream> #include <limits> using namespace std; void Test(const uint16_t cho) { cout << "Okay: " << cho << endl; } int main() { uint64_t blin = std::numeric_limits<uint64_t>::max(); Test(blin); // <-- ругаться нужно тут return 0; } |
Сообщ.
#2
,
|
|
|
Ну, самое простое, это
template <typename T> typename std::enable_if<std::is_same_v<T, uint16_t>, void>::type Test(const T cho) { cout << "Okay: " << cho << endl; } Добавлено Впрочем, на концептах не сильно-то иначе: template <typename T> requires std::same_as<T, uint16_t> void Test(const T cho) { cout << "Okay: " << cho << endl; } |
Сообщ.
#3
,
|
|
|
Т.е. без метапрограммирования никак? Может какие-то ключи компилятора смогут ворнинг на это запилить?
|
Сообщ.
#4
,
|
|
|
Может как-то это можно использовать?
void test_func(uint8_t arg); void test() { uint64_t i = 0; uint8_t test[] = {i}; test_func(test[0]); } file.cpp: In function 'void test()': file.cpp:28:23: warning: narrowing conversion of 'i' from 'uint64_t' {aka 'long long unsigned int'} to 'uint8_t' {aka 'unsigned char'} [-Wnarrowing] 28 | uint8_t test[] = {i}; | ^ |
Сообщ.
#5
,
|
|
|
Цитата Majestio @ В Стандарте чёрным по-англицки написано:Т.е. без метапрограммирования никак? Может какие-то ключи компилятора смогут ворнинг на это запилить? Цитата 7.8 Integral conversions 2 If the destination type is unsigned, the resulting value is the least unsigned integer congruent to the source integer (modulo 2n where n is the number of bits used to represent the unsigned type). [ Note: In a two’s complement representation, this conversion is conceptual and there is no change in the bit pattern (if there is no truncation). —end note ] 3 If the destination type is signed, the value is unchanged if it can be represented in the destination type; otherwise, the value is implementation-defined. Т.е. формально для беззнаковых целых задача в принципе нерешаема, для знаковых она зависит от возможностей реализации. Например, в VC есть ключик -RTCc, но это конкретно его фича. Причём работает она даже для беззнаковых. Где-либо ещё её может и не быть или она управляется иначе. P.S. В документации VS этот ключик даже явно определён как нарушающий Стандарт: Цитата Because /RTCc rejects code that conforms to the standard, it's not supported by the C++ Standard Library. Добавлено P.P.S. Метапрограммирование тем и замечательно, что ты волен напрограммить на нём любые дополнительные аспекты, каковые тебе понадобились, если они не выходят за рамки грамматики языка. Так что не стоит его сторониться. |
Сообщ.
#6
,
|
|
|
Цитата Qraizer @ Т.е. формально для беззнаковых целых задача в принципе нерешаема, для знаковых она зависит от возможностей реализации. Например, в VC есть ключик -RTCc, но это конкретно его фича. Причём работает она даже для беззнаковых. Где-либо ещё её может и не быть или она управляется иначе. Да, печалька. Кажется такой простой и очевидный вопрос |
Сообщ.
#7
,
|
|
|
Цитата Qraizer @ Т.е. формально для беззнаковых целых задача в принципе нерешаема, для знаковых она зависит от возможностей реализации И все же бинго, нашёл! При использовании опции компилятора -Wconversion для GCC, компилятор замечает нарушение беспорядка main.cpp: In function ‘int main()’: main.cpp:12:10: warning: conversion from ‘uint64_t’ {aka ‘long unsigned int’} to ‘uint16_t’ {aka ‘short unsigned int’} may change value [-Wconversion] 12 | Test(blin); // <-- ругаться нужно тут | ^~~~ Qraizer, проверь, пожалуйста, ключики для мелкомягкого компилятора, для моего исходного кода: cl.exe /W2 /EHsc main.cpp Интересно, что твой компилятор найдёт, и что его не устроит. |
Сообщ.
#8
,
|
|
|
/W3
|
Сообщ.
#9
,
|
|
|
Цитата Qraizer @ /W3 |
Сообщ.
#10
,
|
|
|
Если есть желание немного немного помедитировать, я бы предложил посмотреть на это:
// шаблон для безопасного приведения типов U => T // контролирует диапазон целевого типа T и бросает исключение в случае его нарушения значением U template <typename T, typename U> struct SafeCast { static T doIt(const U& val) { using common_type = std::common_type_t<T, U>; // тип, общий для U и T // минимальный максимум для U и T constexpr common_type high= std::min(static_cast<common_type>(std::numeric_limits<T>::max()), static_cast<common_type>(std::numeric_limits<U>::max())), // максимальный минимум для U и T (особая обработка для U и T разной знаковости) low = std::is_signed_v<T> == std::is_signed_v<U> ? std::max(static_cast<common_type>(std::numeric_limits<T>::min()), static_cast<common_type>(std::numeric_limits<U>::min())) : 0; // проверка диапазона if (val > high || val < low) throw std::out_of_range("Value exceeds allowed bounds."); // всё в порядке, каст return static_cast<T>(val); } }; /* Безопасный к переполнению каст арифметических типов. */ template <typename T, typename U> inline T safe_cast(const U& val) { return SafeCast<T, U>::doIt(val); } |
Сообщ.
#11
,
|
|
|
Цитата Qraizer @ Если есть желание немного немного помедитировать, я бы предложил посмотреть на это: Ну это, как говорится, сахар из другого варенья Начиная тему, я просто задался вопросом как зачекать такие "скользкие" моменты. Ну а как там решать дальше - дело десятое. |
Сообщ.
#12
,
|
|
|
Естественно другого. Но тут цимес в том, что чек выполняется не на уровне типов при компиляции, а на уровне значений при выполнении. Это более гибко и возможно более подходит для твоей задачи. Хотя и менее эффективно в плане производительности + исключения ещё ловить.
|
Сообщ.
#13
,
|
|
|
Цитата Qraizer @ Это более гибко и возможно более подходит для твоей задачи Не не не, не для моей. Мне наоборот нужно до рантайма получить сведения о потенциальных дырах. |