🧼C, C++/흑마법

가변인자 프린트함수 ( c++23 )

Mawile 2021. 6. 23.
728x90

 

개발환경 >> Devcpp

언어 >> C++23

운영체제 >> Windows10 home

 

 


안녕하세요!

이번에 c++에서 새로나온 문법 concept아시나요??

이번에 이것에 대해서 공부해보기위해서 만들어본 카테고리입니다.

그것과 동시에 SFINAE도 동시에 병행해서 공부해보려합니다.

그러면 오늘은 이를 이용한 가변인자 프린트함수 만들기

참고자료를 통해서 바로 공부시작해보겠습니다!

 

우선은 concept에 대한 기본적인 문법은 알고있으시다는 전제하에 진행하겠습니다.

소스코드
#include <concepts>
#include <iostream>
 
template<typename T, typename ... U>
concept IsAnyOf = (std::same_as<T, U> || ...);
 
template<typename T>
concept IsPrintable = std::integral<T> || std::floating_point<T> ||
    IsAnyOf<std::remove_cvref_t<std::remove_pointer_t<std::decay_t<T>>>, char, wchar_t>;
 
void println(IsPrintable auto const ... arguments)
{
    (std::wcout << ... << arguments) << '\n';
}
 
int main() { println("Example: ", 3.14, " : ", 42, " : [", 'a', L'-', L"Z]"); }

 

결과값은 다음과 같습니다.

결과값

 

 

자, 그러면 이러한 결과값이 나온 이유를 알기위해서, 

std::same_as<T, U>

std::remove_cvref_t<T>

std::remove_pointer_t<T>

std::decay_t<T>

위에 정의된요소들을 모두 설명하겠습니다.

 

 

std::same_as<T, U>

The concept same_as<T, U> is satisfied if and only if T and U denote the same type.

std::same_as<T, U>는 이름에서도 보이다싶히

T와 U의 같은타입을 표시할때에만 만족합니다.

 

std::remove_cvref_t<T>

If the type T is a reference type, provides the member typedef type which is the type referred to by T with its topmost cv-qualifiers removed. Otherwise type is T with its topmost cv-qualifiers removed.

std::remove_cvref_t<T>는 쉽게설명하자면,

T에 들어오는 레퍼런스를 제거시켜버립니다.

한마디로 int가 들어오든, int&&가들어오든, int&가 들어오든 같은타입으로 간주하게됩니다.

 

std::remove_pointer_t<T>

Provides the member typedef type which is the type pointed to by T, or, if T is not a pointer, then type is the same as T.

std::remove_pointer_t<T>는 T에 포인터형식이 들어왔을때에 삭제시켜버립니다.

 

std::decay_t<T>

Applies lvalue-to-rvalue, array-to-pointer, and function-to-pointer implicit conversions to the type T, removes cv-qualifiers, and defines the resulting type as the member typedef type.

std::decay_t<T>는 T에 들어오는 타입의 레퍼런스를 제거시킵니다.

 

 

설명
#include <concepts>	// 개념
#include <iostream>	// c++ 표준입출력
 
template<typename T, typename ... U> //T와 여러개의 타입 U

// T와 뒤에따라오는 여러개의 U를 묶고, 같은 타입인지 확인한다.
concept IsAnyOf = (std::same_as<T, U> || ...);
 
template<typename T>

// T의 타입이 실수, 정수형이면 가능, 또는 T타입의 참조성과 포인터를 제거한 타입이 char이나 wchar_t
// 일경우 가능 (한마디로 배열, 포인터 모두 출력가능하도록 하게함.)
concept IsPrintable = std::integral<T> || std::floating_point<T> ||
    IsAnyOf<std::remove_cvref_t<std::remove_pointer_t<std::decay_t<T>>>, char, wchar_t>;
 
 // IsPrintable은 실수,정수, 포인터,배열 모두 입력가능한 가변인수를 갖는 개념이다.
void println(IsPrintable auto const ... arguments)
{
	// 일단 모두 출력한다음 다음칸으로 건너띄기위해, ()를 묶어준다.
    (std::wcout << ... << arguments) << '\n';
}
 
int main() { println("Example: ", 3.14, " : ", 42, " : [", 'a', L'-', L"Z]"); }

 

우선 위 코드에서 핵심은 std::same_as입니다.

이에 대한 이해를 쉽게하기 위해, 저가 비슷하지만 좀 다른 코드를 직접 짜보았습니다.

 

살짝 변형한 코드
#include <concepts>
#include <iostream>

template<class _MAIN, class ... _SUB>
concept _SameTrue = (std::same_as<_MAIN, _SUB> && ... ); // 이 부분을 &&로 바꿨습니다.

template<class _MAIN>
concept PRINTABLE =    std::integral<_MAIN>
					|| std::floating_point<_MAIN>
					|| _SameTrue<std::remove_cvref_t<
					std::remove_pointer_t<
					std::decay_t<_MAIN>>>, char /*, wchar_t를 붙이면 오류*/ >;

void my_println(PRINTABLE auto const ... args){ (std::wcout << ... << args) << '\n'; }

int main(){
	my_println("test", ' ', 3.14, /*L를 붙이면 오류*/"test");
}

 

만약에 same_as에 II를 하면은 뒤에 char이 오건, wchar_t가 오건,

하나라도 일치하면 값은 정상적으로 들어가게 됩니다.

하지만 &&를 하면은 뒤에 char이 오는 동시에 wchar_t가 와야만 정상적으로 값이 들어가게 됩니다.

한마디로 _MAIN과 뒤에 따라오는 가변인수 _SUB들을 모두 비교해보게되는겁니다.

 

 

직접 응용해서 짜본 소스코드 ( 가변인수를 받아 튜플로 반환 )
#include <concepts>
#include <iostream>
#include <tuple>

template<class _MAIN, class ... _SUB>
concept _SameTrue = (std::same_as<_MAIN, _SUB> || ... );

template<class _MAIN>
concept _ANYOF =    std::integral<_MAIN>
					|| std::floating_point<_MAIN>
					|| _SameTrue<std::remove_cvref_t<
					   std::remove_pointer_t<
					   std::decay_t<_MAIN>>>, char, wchar_t>;

auto const _GetTuple(_ANYOF auto const ... args){
	return std::make_tuple(args...);
}

int main(){
	auto[a1, a2, a3] = _GetTuple("test", 3.14, 10);
	(std::wcout << a1 << a2 << a3) << '\n';
}

 

끝!

 

 

 

참고자료

https://en.cppreference.com/w/cpp/concepts/same_as

 

std::same_as - cppreference.com

template < class T, class U > concept same_as = /* see below */; (since C++20) The concept same_as is satisfied if and only if T and U denote the same type. std::same_as subsumes std::same_as and vice versa. [edit] Possible implementation namespace detail

en.cppreference.com

 

 

 

 

 

728x90

댓글