C++로 재귀하면서 noexcept한 수치 입력 함수를 만든다

15486 단어 C++C++11stdin

소개



C 언어로 안전하게 표준 입력으로부터 수치를 습득
라는 것을 썼는데 C++에서는 어떨까 봐 간다.

다행히 C와 달리 C++의 표준 입출력은 우수하다.

C++에서 숫자 입력의 열매에 흔한 예
int input_num;
std::cin >> input_num;

C처럼 표준 입출력이 라스보스, 따위는 없다.

그러나 클래스에 대한 이해와 플래그에 대한 이해는 여전히 필요합니다.

C 언어로 안전하게 표준 입력으로부터 수치를 습득 에서 소개한 에러시는 재귀한다, 라고 하는 것은 실은 초보자에게도 알기 쉬운 것이 아닐까? 라고 생각하기 때문에 그것을 써 본다. 물론 예외라는 초보자에게는 잘 모르는 것은 의식하지 않고 끝나도록, noexcept 지정을 붙인다.

주의점



arithmetic에 한정





아리스-···가 아닌, arithmetic가 아닌 것에 대한 operator>> 가 불리지 않게, TMP로 연주할 필요가 있다. 물론 SFINAE를 사용한다. 방법은
std::enable_if를 사용하여 오버로드 할 때 enabler를 사용합니까?std::nullptr_t 형식을 사용하는 것을 사용합니다.

int8_t / uint8_t 형을 위해서



int8_t/uint8_t 형은 일반적으로 char/unsigned char 에 typedef 되고 있어 operator>> 있다.char/singed shar/unsigend char 형은 sizeof 연산자로 1이 되는 것을 이용해 쓰면 좋지만, 지금의 C++에는 static_if 와 같은 편리한 기능은 없기 때문에 TMP 할 필요가 있다.
이때 std::conditional 를 사용하면 매우 읽기 어려워지므로,
메모:std::conditional에서 if~else if~else 같은 것을 하려고 한다고 보기 어렵다
방법을 사용합니다.

아티팩트


#include <iostream>
#include <exception>
#include <stdexcept>
#include <type_traits>
#include <limits>
/**
@brief  \~japanese 複数条件のあるTMP用if
\~english multi-conditional if struct for TMP.
\~japanese  std::enable_ifと組み合わせて使います。
\~english   This class is used in conjunction with std::enable_if.
\~
@code
template<typename T>
using bar = first_enabled_t<
    std::enbale_if<cond1, type1>,
    std::enbale_if<cond2, type2>,
    std::enbale_if<cond3, type3>,
    default_type
>;
@endcode
*/
template<typename ...Args>
struct first_enabled {};

template<typename T, typename ...Args>
struct first_enabled<std::enable_if<true, T>, Args...> { using type = T; };
template<typename T, typename ...Args>
struct first_enabled<std::enable_if<false, T>, Args...> : first_enabled<Args...> {};
template<typename T, typename ...Args>
struct first_enabled<T, Args...> { using type = T; };

template<typename ...Args>
using first_enabled_t = typename first_enabled<Args...>::type;

//! for int8_t/uint8_t
template<typename T, std::enable_if_t<std::is_arithmetic<T>::value, std::nullptr_t> = nullptr>
using arithmetic_t = first_enabled_t <
    std::enable_if<1 != sizeof(T), T>,
    std::enable_if<std::is_signed<T>::value, int>,
    unsigned int
>;
template<typename T_> using limit = std::numeric_limits<T_>;//create new type. C++11:alias declaration
/**
 * @brief 標準入力から入力を受ける
 * @details [long description]
 * 
 * @param echo_str 入力を受ける前に表示する文字列。表示しない場合はnullptrか空白文字のみで構成された文字列へのポインタを渡す
 * @param max 入力値を制限する。最大値を指定
 * @param min 入力値を制限する。最小値を指定
 * @return 入力した数字
 * @exception none
 */
template<typename T, std::enable_if_t<std::is_arithmetic<T>::value, std::nullptr_t> = nullptr>//Tが整数か浮動小数点型でないならばコンパイルエラーを出す
T input(const char* echo_str, const T max = limit<T>::max(), const T min = limit<T>::lowest()) noexcept {
    arithmetic_t<T> buf;
    try {
        std::cin.exceptions(std::ios::failbit | std::ios::badbit);
        if (nullptr != echo_str && '\0' != echo_str[0]) std::cout << echo_str << std::endl;//文字列が空じゃなければ出力
        std::cin >> buf;//入力を受ける
        if (max < buf || buf < min) throw std::out_of_range("input is iligal");//範囲チェック
    }
    catch (std::exception& er) {
        std::cerr << er.what() << std::endl;//エラーメッセージ表示
        return input("再入力してください。", max, min);//エラー時は再帰する方向で
    }
    return static_cast<T>(buf);
}

사용법


int main(){
    const auto input1 = input("数字を1~50の間で入力してください", 50, 1);//input1はint型
    const auto input2 = input("数字を1~50の間で入力してください", 50U, 1U);//input2はunsigned int型
    const auto input3 = input<unsigned long long>("数字を1~50の間で入力してください", 1200, 3);//input3はunsigned long long型
    const auto input4 = input<unsigned short>("数字を入力してください");//input4はunsigned short型
    return 0;
}

결론



내부 실장은 예외를 남용한 것이 되어, 약간 무거울지도 모르지만, 원래 표준 입출력에 속도를 구해도 어쩔 수 없기 때문에 이것으로 좋다고 한다.

추가



잘 보면 이 녀석 bool에서도 움직인다···.

좋은 웹페이지 즐겨찾기