Modern C++ 무조건 써야해?
신미영
Naver
CONTENTS
1. Modern C++ 소개
2. Why use ?
3. Chromium 의 modern C++ 리뷰 과정
4. C++ 11 & 14 features/libraries 허용과 금지 사이
5. Conclusion
6. Q&A
1.
modern C++ 소개
1.1 modern C++ 이란?
here
1.2 modern C++ 컴파일러 지원 현황
https://en.cppreference.com/w/cpp/compiler_support
2.
Why
use?
2.1 왜 써야하죠?
1. 안정성 : Smart Pointer
2. 성능 : Move Semantics
3. 간결 : auto, lambda
4. 병렬화 : Thread
5. 다양한 기능들
…
2.2 Copy vs Move 성능 비교 테스트 코드
template <typename T>
void measure (T& t) {
T t1(t); // copy
T t2(std::move(t)); // move
}
measure(T);
측정 환경 – online compile & run with option GCC 7.1 (https://en.cppreference.com)
코드 출처 - http://modernescpp.com/index.php/copy-versus-move-semantic-a-few-numbers
2.3 Copy vs Move 성능 비교 결과 (1/2)
SIZE = 10,000
std::array<int, SIZE> myArray; // +1.2배
std::vector<int> myVec(SIZE); // +53.88배
std::deque<int>myDec(SIZE); // +65.60배
std::list<int>myList(SIZE); // +705.31배
std::string myString(SIZE,' '); // +54.95배
MOVE
WIN
2.3 Copy vs Move 성능 비교 결과 (2/2)
SIZE = 10,000
std::map<int,int>myMap;
for (auto i= 0; i <= SIZE; ++i) myMap[i]= i; // +925.01 배
std::unordered_map<int,int>myUMap;
for (auto i= 0; i <= SIZE; ++i) myUMap[i]= i; // 896.42배
MOVE
WIN
2.4 그럼 쓰면 되지 무엇이 문제?
1. 내 컴파일러들은 빌드를 잘해내는가 ?
2. 빌드 후 실행 시 예상대로 동작하는가 ?
3. 지원해야 할 환경들(OS, 컴파일러 등등)에서 모두 동일하게 동작하는가?
4. 기존 utility 들을 대체할 가치가 있는가 ?
5. 실수를 유발할 가능성이 높은 코드로 작성되지 않는가?
6. 코드 가독성이 떨어지지 않는가?
7. 기존 코딩 가이드와 충돌하지는 않는가?
…
=> 우리의 고민을 대신 누군가 해주었다면?
3.
Chromium 의
modern C++ 리뷰 과정
리뷰 과정
1. cxx@chromium.org 로 항목 제안
or https://groups.google.com/a/chromium.org/forum/#!forum/cxx 포스트
2. Chromium community 를 통해 자유롭게 공개 논의
3. 최종 결론에 다다르면 chromium repo 에
https://chromium.googlesource.com/chromium/src/+/master/styleguide/c++/c++11.
html 파일 수정 업데이트
4. https://chromium-cpp.appspot.com/ 에 반영됨
리뷰 과정 예제
Oct.2018 활동 통계
4.
C++ 11 & 14 features/libraries
허용과 금지 사이
4.1 Smart Pointer (1/6)
Unique Pointers (+11)
std::unique_ptr<new Widget> upw1(new Widget);
std::make_unique (+14)
auto upw2(std::make_unique<Widget>());
std::vector<std::unique_ptr<Widget>> v;
v.push_back(std::make_unique<Widget>());
v.clear(); // Widget 메모리 해제
허용
금지
4.1 Smart Pointer (2/6)
Shared Pointers (+14)
std::shared_ptr<new Widget> spw1(new Widget);
std::shared_ptr 은 제어 블럭이 여러개 생성될 수 있는 이슈가 있음.
chromium 은 base::scoped_refptr 라는 자체 shared pointer 를 사용함
* Weak Pointer 는 아직 논의 중
허용
금지
Control Block
T Object
4.1 Smart Pointer (3/6)
Shared Pointers Control Block
허용
금지
Reference Count
Weak Count
Other Data
(e.g., custom deleter,
allocator, etc.)
Ptr to T
Ptr to Control Block
std::shared_ptr<T>
4.1 Smart Pointer (4/6)
Shared Pointer 의 여러번 Control Block 생성되는 이슈 발생
auto rpw = new Widget;
std::shared_ptr<Widget> spw1(rpw);
std::shared_ptr<Widget> spw2(rpw);
허용
금지
rpw pointer
control block
ref count = 1
spw1 spw2
control block
ref count = 1
4.1 Smart Pointer (5/6)
shared_ptr control block 이 여러번 생성되는 이슈 회피 방법
- raw pointer 변수로부터 shared_ptr가 생성되지 않도록 함
std::shared_ptr<Widget> spw1(new Widget);
auto spw2 = std::make_shared<Widget>();
허용
금지
4.1 Smart Pointer (6/6)
chromium 이 사용하는 base::scoped_refptr 은?
class 내부에 ref count 존재
- 해당 클래스가 여러 쓰레드에서 접근된다면
- 해당 클래스가 하나의 쓰레드에서만 접근된다면
허용
금지
std::atomic_int ref_count_;
RefCountedThreadSafeBase
+AddRef
+Release
RefCountedThreadSafeClass A
uint32_t ref_count_;
RefCountedBase
+AddRef
+Release
RefCountedClass B
4.2 Move Semantics (1/10)
RValue References (+14)
T(T&& t)
T& operator=(T&& t)
template <typename T> void Function(T&& t) { ... }
이동 생성자 / 이동 연산자 / perfect forward 사용 권장
만약 overloading 을 줄이기 위한 용도로 사용한다면, 예상하지 않은 값은 처리해야함.
<template typename T,
typename = std::enable_if_t<!std::is_base_of<Person, std:decay_t<T>::value &&
!std::is_integral<std::remove_reference_t<T>>::value>>
explicit Person(T&& n) : name(std::forward<T>(n); {
static_assert(std::is_constructible<std::string, T>::value, “”Parameter…………..”);
} *출처 : Modern Effective C++ 저서
허용
금지
아~ 복잡해~
4.2 Move Semantics (2/10)
Move Semantics (+11)
std::move()
Rvalue ref 로 캐스팅. 효율적 이동 연산을 위해 사용
Forwarding references (+11)
std::forward()
Lvalue ref 는 Lvalue Ref, Rvalue ref 는 Rvalue Ref 로 캐스팅. 완벽 전달자
허용
금지
4.2 Move Semantics (3/10)
● 내 클래스가 Move ctor / Move assign 가지는 경우
○ 아무런 코드 추가 없이 컴파일러가 알아서 자동 생성
○ 클래스내 Move ctor / Move assign 선언에 = default; 사용하여 컴파일러가 구현부
생성
○ 클래스내 Move ctor / Move assign 선언 및 구현부 추가
● 컴파일러에 의한 이동 연산자에 대한 기본 함수 생성 조건
○ 클래스에 Copy ctor / Copy assign 선언이 없어야 함
○ 클래스에 Move ctor / Move assign 선언이 없어야 함
○ 클래스에 소멸자 선언이 없어야 함
허용
금지
4.2 Move Semantics (4/10)
Default Function Creation (+11)
Function(arguments) = default;
컴파일러가 기본 함수 생성하도록 명시
Function Suppression
Function(arguments) = delete;
이동은 허용하지만 복사는 허용하지 않는다면, 하기 같은 매크로 이용하면 좋을 듯
#define DISALLOW_COPY_AND_ASSIGN(TypeName) 
TypeName(const TypeName&) = delete; 
TypeName& operator=(const TypeName&) = delete
허용
금지
4.2 Move Semantics (5/10)
Emplacement methods for containers (+11)
emplace(), emplace_back(), emplace_front(), emplace_hint()
삽입 push_back 보다 생성삽입 emplace_back 가 성능이 좋을 수 있음.
Emplacement 또다른 장점은 한번에 여러개 인수 전달 가능
void emplace_back(Args&&...);
v.emplace_back(“a”, “b”, “c”);
=> push_back 은?
v.push_back(“a”); v.push_back(“b”); v.push_back(“c”);
허용
금지
4.2 Move Semantics (6/10)
push_back vs emplace_back
std::vector<std::string> v;
v.push_back(“abc”); // 임시객체 String 생성 -> move 생성자 -> 임시객체 소멸
v.emplace_back(“abc”) // char* 그대로 전달 -> String 생성 삽입
String s(“abc”);
v.push_back(s); // 성능 차이 없음
v.emplace_back(s); // 성능 차이 없음
허용
금지
4.2 Move Semantics (7/10)
noexcept Specifier (+11)
void f() noexcept;
compiler 에게 최적화할 수 있는 기회 제공 (호출 스택이 풀릴 여지가 없다라고 과정하기 때문)
move constructor 에서 적극적 사용 권장
=> std::vector::push_back 을 포함한 여러 표준 라이브러리 함수에서 move 연산이 수행됨
* move 연산자에 noexcept 명시자가 없으면, exception 이 발생할 수 있는 코드라 간주, 이전
상태로 돌릴 수 있는 복사 연산을 선택함.
Chromium 사용 예제)
GURL(GURL&& other) noexcept;
GURL& operator=(GURL&& other) noexcept;
허용
금지
4.2 Move Semantics (8/10)
Move Iterator Adaptor (+11)
std::make_move_iterator()
반복문을 통해 객체들을 copy 대신 move 처리
std::unique_ptr 같은 move only 타입들을 담고 있는 container 들 이동할 때 유용함
허용
금지
4.2 Move Semantics (9/10)
Move Iterator Adaptor(+11) 사용 예제
std::list<std::string> s{"one", "two", "three"};
std::vector<std::string> v1(s.begin(), s.end()); // copy
std::vector<std::string> v2(std::make_move_iterator(s.begin()),
std::make_move_iterator(s.end())); // move
v1, v2 는 "one", "two", "three"
s 는 empty
허용
금지
4.2 Move Semantics (10/10)
Ref-qualified Member Functions (+11)
class T {
void f() & {}
void f() && {}
};
Chromium 의 경우) 지나치게 모호한 성격을 띄기 때문에 OWNERS 의 승인 후 사용
가능
T t;
t.f(); // first
T().f(); // second
std::move(t).f(); // second
허용
금지
4.3 Type Deduction (1/3)
Automatic Types (+11)
auto a = 1 + 2;
readability 를 고려해서 사용
- raw pointer 에서는 auto* 를 사용 (auto 가 smart pointer 인지 raw pointer 인지 알수가 없음)
Trailing Return Types (+11)
auto function declaration -> return_type
readability 가 좋은 곳에서만 사용할 것
허용
금지
4.3 Type Deduction (2/3)
Declared Type Accessor (+11)
decltype(expression)
bool f(const Widget& w)
auto w1 = w; // auto 는 Widget 로 deduction 됨
decltype(w) // decltype 은 const Widget& 으로 deduction 됨
허용
금지
4.3 Type Deduction (3/3)
Function return type deduction (+14)
auto f() { return 42; }
decltype(auto) g() { return 42; }
Chromium 의 경우) abstract template code 주요 사용할 것을 권장
- decltype(auto) 는 템플릿된 forwarding/wrapper function 에서 사용
template <typename T> decltype(auto) Unwrap(T&& o) {
return Unwrapper<T>::Unwrap(std::forward<T>(o));
}
- 이외의 경우 auto 를 사용
template <typename F> auto BindLambdaForTesting(F&& f)
허용
금지
4.4 Lambdas (1/5)
Lambda expressions (+11)
[captures](params) -> ret { body }
default captures ([=], [&]) 에서 조심하게 사용해야함.
Chromium 의 경우) 다음과 같이 제약함
- 스택 프레임 밖 lifecycle 를 가지는 로직에서는 어떤 capturing 값도 bind 하거나,
store 하지 않아야 함
- 만약 캡쳐가 필요하면 base::Bind 로 감싸서 base::Callback을 등록하여 사용.
- 캡쳐가 없더라도 base::Bind 와 함께 사용 가능
허용
금지
4.4 Lambdas (2/5) - Chromium 사용 예제
• 캡쳐도 없고 스택 프레임 안에서 동작하는 경우
std::sort(v.begin(), v.end(), [](int x, int y) {
return Weight(x) < Weight(y);
});
• Lambda 를 base::Bind 로 감싼 경우
Return base::Bind(
[](Status* status, bool accepted) {
*status = accepted ? Status::ACCEPTED : Status::CANCELLED;
}, status);
• this 를 캡쳐해야하고 스택 프레임 밖에서(또는 다른 쓰레드에서) 동작하는 경우
io_task_runner_->PostTask(
FROM_HERE, base::Bind(&ExternalDataUseObserver::OnDataUseBatchComplete,
GetWeakPtr()));
4.4 Lambdas (3/5)
Bind Operations
std::bind(function, args, ...)
대신 base::Bind 사용 => Lifecyle 관리에 유용함
허용
금지
base::Bind(&Widget::Function, GetWeakPtr()); std::weak_ptr<Widget> thisWeakPtr(shared_from_this());
std::bind(&StaticFuntion, thisWeakPtr);
staticFunction(std::weak_ptr<Widget> thisWeakPtr) {
Widget w = thisWeakPtr.lock();
if (!w) return;
w->Function();
}
4.4 Lambdas (4/5)
Function Objects
std::function
대신 base::Callback 사용
- Class 의 refcounting 과 weak ptr 로 지원됨
허용
금지
4.4 Lambdas (5/5)
Generic lambdas (+14)
[](const auto& x) { ... }
람다 argument type 을 위한 auto 사용
허용
금지
4.5 Thread (1/3)
thread Libraries (+11)
<thread> <future> <mutex>, <condition_variable>..
Chromium 의 경우)
- base::Thread 가 MessageLoop 와 이미 밀접하게 붙어있어 대체하기 어렵다 판단
- locking/synchronization 같은 기존 클래스를 표준 mutex, unique_lock 등으로 교체
가능한지 검토 예정
허용
금지
4.5 Thread (2/3)
Atomics (+11)
std::atomic<types>
for lockless concurrent programming
원자적 연산 가능 (RMW (Read/Modify/Write)
복사나 배정 불가능.
=> load() 와 store() 멤버 함수들 통해 배정 지원.
memory-ordering 명시 가능.
: load/store 에 명시된 메모리 순서에 따라,
쓰기 순서를 보장하거나, 쓰여지면 읽기를 수행하는 등
메모리 접근에 대한 순서를 명시할수 있음
허용
금지
Thread A
Thread B
Integer 10
read read
10
10
10 + 1 = 11
increment
10 + 1 = 11
increment
11 11
write write
time
4.5 Thread (3/3)
thread_local storage class (+11)
thread_local int foo = 1;
Chromium 의 경우) WTF::ThreadSpecific 을 사용했을 때 보다 성능 측면에서 2.5x 배
빠름
그러나 Android M 이전 버전에서 이상 동작.
Mac 에서 TLS object 가 destroy 된 이후 재 접근시 새로 생성되는 이슈 있음.
TLS object 가 POD type 이고 re-initialize 가 되어도 상관 없으면 사용해도 됨.
Chromium 의 경우) /base/threading/sequence_local_storage_slot.h 사용 권장
허용
금지
4.6 다루지 못한 것들
1. 나머지 금지 항목들 / 허용하지만 조심히 다루어야 할 항목들
=> Appendix 참조
2. 나머지 허용 항목들 / 아직 논의 중인 항목들
자세한 내용들은 https://chromium-cpp.appspot.com/ 참조
5.
Conclusion
5. Conclusion
1. Modern C++ 사용하자
- 성능 향상, 다양한 기능, 안정성, 간결한 코딩
2. 사용전에 지원 환경/동작 유무 등을 모두 검토하자
3. feature 특징에 따라 조심해서 사용하자
4. 내 프로젝트에도 C++ Coding Guide 를 세워보자
5. Chromium base library 참고해볼만하다
Q & A
질문은 Slido에 남겨주세요.
sli.do
#deview
TRACK 4
Thank you
Appendix
A1. 이외 금지 (1/8)
Inline Namespaces (+11)
namespace outer {
inline namespace v2 { void Func() { return std::string("v2 Func"); } }
namespace v1 { void Func() { return std::string("v1 Func"); } }
} // namespace outer
인라인 네임스페이스의 멤버는 부모 네임스페이스의 멤버로 처리됨.
즉 outer::v2::Func 가 아니라 outer ::Func 로 직접 호출할 수 있음
큰 버전 정책에 용이하나, 불명확한 namespace 사용으로 혼란스러움.
허용
금지
A1. 이외 금지 (2/8)
long long (+11)
64 bits integer
third party code 와 호환하기 어려움
<stdint.h> 내에 int16_t, uint32_t, int64_t 같은 타입을 사용
허용
금지
A1. 이외 금지 (3/8)
User-Defined Literals (+11)
long double operator "" _w(long double);
unsigned operator "" _w(const char*);
1.2_w; // calls operator "" _w(1.2L)
12_w; // calls operator "" _w("12")
구글 c++ style 가이드에서 금지
경험이 많은 C++ 개발자에게도 낯선 새로운 구문 형태는 혼란을 가중시킴
허용
금지
A1. 이외 금지 (4/8)
Ratio Template Class (+11)
std::ratio<numerator, denominator>
std::cout << ratio_<1,2> + ratio_<1,3> << std::endl;
Output : ⅚
google c++ style 에서 금지됨
해당 템플릿이 더 많은 템플릿 인터페이스들과 연결되어 있어 우려됨
허용
금지
A1. 이외 금지 (5/8)
C Floating-Point Environment (+11)
<cfenv>, <fenv.h> header
부동 소수점 상태 플래그 및 C 호환성을 위한 제어 모드 제공
google c++ style 가이드에서 금지됨
많은 컴파일러들이 제대로 지원하고 있지 않음
허용
금지
A1. 이외 금지 (6/8)
Regular Expressions (+11)
<regex>
별다른 논의는 없었음.
Chromium내에 많은 regular expression libraries 가 기존재함으로 기존 것 사용.
혹은 thirdparty/re2 (by google) 사용
허용
금지
A1. 이외 금지 (7/8)
std::chrono literals (+14)
<chrono> header & std::chrono literals
using namespace std::chrono_literals;
auto timeout = 30s;
Date and time utilities
별다른 논의는 없었으나 base/Time 유지하도록 함
허용
금지
A1 이외 금지 (8/8)
Exceptions
<exception>
google c++ style 가이드에서 금지되어 있음
기존 코드에 적용하는 것보다 새로운 프로젝트에 사용하는게 적절함
exception 이 적용된 프로젝트를 기존 exception-free 코드에 적용하면 통합에 문제가
발생할 수 있음.
허용
금지
A2. Alignment
Alignment Features (+11)
struct alignas(64) Empty64 {}; alignof(Empty64) => 64
alignas 는 export / #pragma pack 에서 호환되지 않음.
- ALIGNAS() 대신 사용 (base/compiler_specific.h)
Aligned storage (+11)
std::aligned_storage<len, align>
MSVC 2017 에서 align 8 byte 이상 값을 처리 못함
Alignment Features 대신 사용
std::aligned_storage<10, 64> => alignas(64) char data[10];
허용
금지
A3. Allow, But Be careful
Non-Static Class Member Initializers (+11)
class C {
type var = value;
C() // copy-initializes var
}
기본 사용 허용. 단, 하기 경우 제외
생성자 안에서 다른 멤버 초기화를 위해 필요한 경우
초기화가 복잡한 경우
허용
금지
A3. Allow but Be careful
Standard Integers (+11)
Typedefs within <stdint.h> and <inttypes>
int16_t, uint32_t, int64_t…. 등 size safe 한 타입 사용 권장
short, unsigned long long 도 허용, 단 size 에 대해 명확할 경우에만.
unsigned 보다는 signed type 을 사용할 것
- unsigned 는 bit 표현을 위한 목적일 때만 사용
- negative 값이 절대 없다고 가정하여 unsigned를 사용하지는 말 것.
signed 를 사용하되 Assertion으로 처리할 것
허용
금지
A3. Allow but Be careful
Raw string literals (+11)
string var=R"(raw_string)";
GCC 4.9+ 보다 오래된 버전이라면, lexer(=lexical analyzer) bug 존재
=> new line 이 포함된 literals 일 때 이슈 있음
#pragma omp parallel num_threads(sizeof R"(
abc
)")
허용
금지
A3. Allow but Be careful
Type Aliases ("using" instead of "typedef") (+11)
using new_alias = typename
typedef 대신 사용
C 와 호환되어야 할 헤더의 경우 제외
허용
금지
A3. Allow but Be careful
Constant Expressions (+11)
constexpr int f();
constexpr bool b1 = noexcept(f());
Relaxed constant expressions (+14)
constexpr 함수에서 return 이외 조건문 추가 등 및 복잡한 구현 가능
VS 2017 constexpr 함수내 loop 사용 지원됨 (17년 9월말)
변수에서는 const 보다 constexpr 를 사용, 함수에서는 조심스럽게
- 복잡성을 가지는 함수에서는 사용을 피할 것.
- 함수를 inline 으로 강제하기 위한 목적으로 사용하지 말 것
허용
금지
A3. Allow but Be careful
Uniform Initialization Syntax (+11)
type name {[value ..., value]};
심플한 초기화를 위해서는 assignment syntax 사용
std::pair<bool, double> p = {true, 2.0};
std::vector<std::string> v = {"one", "two", "three"};
특별한 로직, 명시적 생성자, reader 에게 직관적으로 심플하지 않을 때 constructor syntax 사용
MyClass c(1.7, false, "test");
std::vector<double> v(500, 0.97); // Creates 500 copies of the provided initializer
uniform init syntax 사용. (= 없는 {} 사용)
C c{true}; // Cannot use '=' since C() is explicit (and "()" is invalid syntax here)
auto 와 섞어서 쓰지 말 것
- auto x{1} => 의도가 int 로 보이지만 std::initializer_list<int> 로 동작됨)
허용
금지

[143] Modern C++ 무조건 써야 해?

  • 1.
    Modern C++ 무조건써야해? 신미영 Naver
  • 2.
    CONTENTS 1. Modern C++소개 2. Why use ? 3. Chromium 의 modern C++ 리뷰 과정 4. C++ 11 & 14 features/libraries 허용과 금지 사이 5. Conclusion 6. Q&A
  • 3.
  • 4.
    1.1 modern C++이란? here
  • 5.
    1.2 modern C++컴파일러 지원 현황 https://en.cppreference.com/w/cpp/compiler_support
  • 6.
  • 7.
    2.1 왜 써야하죠? 1.안정성 : Smart Pointer 2. 성능 : Move Semantics 3. 간결 : auto, lambda 4. 병렬화 : Thread 5. 다양한 기능들 …
  • 8.
    2.2 Copy vsMove 성능 비교 테스트 코드 template <typename T> void measure (T& t) { T t1(t); // copy T t2(std::move(t)); // move } measure(T); 측정 환경 – online compile & run with option GCC 7.1 (https://en.cppreference.com) 코드 출처 - http://modernescpp.com/index.php/copy-versus-move-semantic-a-few-numbers
  • 9.
    2.3 Copy vsMove 성능 비교 결과 (1/2) SIZE = 10,000 std::array<int, SIZE> myArray; // +1.2배 std::vector<int> myVec(SIZE); // +53.88배 std::deque<int>myDec(SIZE); // +65.60배 std::list<int>myList(SIZE); // +705.31배 std::string myString(SIZE,' '); // +54.95배 MOVE WIN
  • 10.
    2.3 Copy vsMove 성능 비교 결과 (2/2) SIZE = 10,000 std::map<int,int>myMap; for (auto i= 0; i <= SIZE; ++i) myMap[i]= i; // +925.01 배 std::unordered_map<int,int>myUMap; for (auto i= 0; i <= SIZE; ++i) myUMap[i]= i; // 896.42배 MOVE WIN
  • 11.
    2.4 그럼 쓰면되지 무엇이 문제? 1. 내 컴파일러들은 빌드를 잘해내는가 ? 2. 빌드 후 실행 시 예상대로 동작하는가 ? 3. 지원해야 할 환경들(OS, 컴파일러 등등)에서 모두 동일하게 동작하는가? 4. 기존 utility 들을 대체할 가치가 있는가 ? 5. 실수를 유발할 가능성이 높은 코드로 작성되지 않는가? 6. 코드 가독성이 떨어지지 않는가? 7. 기존 코딩 가이드와 충돌하지는 않는가? … => 우리의 고민을 대신 누군가 해주었다면?
  • 12.
  • 13.
    리뷰 과정 1. cxx@chromium.org로 항목 제안 or https://groups.google.com/a/chromium.org/forum/#!forum/cxx 포스트 2. Chromium community 를 통해 자유롭게 공개 논의 3. 최종 결론에 다다르면 chromium repo 에 https://chromium.googlesource.com/chromium/src/+/master/styleguide/c++/c++11. html 파일 수정 업데이트 4. https://chromium-cpp.appspot.com/ 에 반영됨
  • 14.
  • 15.
    4. C++ 11 &14 features/libraries 허용과 금지 사이
  • 16.
    4.1 Smart Pointer(1/6) Unique Pointers (+11) std::unique_ptr<new Widget> upw1(new Widget); std::make_unique (+14) auto upw2(std::make_unique<Widget>()); std::vector<std::unique_ptr<Widget>> v; v.push_back(std::make_unique<Widget>()); v.clear(); // Widget 메모리 해제 허용 금지
  • 17.
    4.1 Smart Pointer(2/6) Shared Pointers (+14) std::shared_ptr<new Widget> spw1(new Widget); std::shared_ptr 은 제어 블럭이 여러개 생성될 수 있는 이슈가 있음. chromium 은 base::scoped_refptr 라는 자체 shared pointer 를 사용함 * Weak Pointer 는 아직 논의 중 허용 금지
  • 18.
    Control Block T Object 4.1Smart Pointer (3/6) Shared Pointers Control Block 허용 금지 Reference Count Weak Count Other Data (e.g., custom deleter, allocator, etc.) Ptr to T Ptr to Control Block std::shared_ptr<T>
  • 19.
    4.1 Smart Pointer(4/6) Shared Pointer 의 여러번 Control Block 생성되는 이슈 발생 auto rpw = new Widget; std::shared_ptr<Widget> spw1(rpw); std::shared_ptr<Widget> spw2(rpw); 허용 금지 rpw pointer control block ref count = 1 spw1 spw2 control block ref count = 1
  • 20.
    4.1 Smart Pointer(5/6) shared_ptr control block 이 여러번 생성되는 이슈 회피 방법 - raw pointer 변수로부터 shared_ptr가 생성되지 않도록 함 std::shared_ptr<Widget> spw1(new Widget); auto spw2 = std::make_shared<Widget>(); 허용 금지
  • 21.
    4.1 Smart Pointer(6/6) chromium 이 사용하는 base::scoped_refptr 은? class 내부에 ref count 존재 - 해당 클래스가 여러 쓰레드에서 접근된다면 - 해당 클래스가 하나의 쓰레드에서만 접근된다면 허용 금지 std::atomic_int ref_count_; RefCountedThreadSafeBase +AddRef +Release RefCountedThreadSafeClass A uint32_t ref_count_; RefCountedBase +AddRef +Release RefCountedClass B
  • 22.
    4.2 Move Semantics(1/10) RValue References (+14) T(T&& t) T& operator=(T&& t) template <typename T> void Function(T&& t) { ... } 이동 생성자 / 이동 연산자 / perfect forward 사용 권장 만약 overloading 을 줄이기 위한 용도로 사용한다면, 예상하지 않은 값은 처리해야함. <template typename T, typename = std::enable_if_t<!std::is_base_of<Person, std:decay_t<T>::value && !std::is_integral<std::remove_reference_t<T>>::value>> explicit Person(T&& n) : name(std::forward<T>(n); { static_assert(std::is_constructible<std::string, T>::value, “”Parameter…………..”); } *출처 : Modern Effective C++ 저서 허용 금지 아~ 복잡해~
  • 23.
    4.2 Move Semantics(2/10) Move Semantics (+11) std::move() Rvalue ref 로 캐스팅. 효율적 이동 연산을 위해 사용 Forwarding references (+11) std::forward() Lvalue ref 는 Lvalue Ref, Rvalue ref 는 Rvalue Ref 로 캐스팅. 완벽 전달자 허용 금지
  • 24.
    4.2 Move Semantics(3/10) ● 내 클래스가 Move ctor / Move assign 가지는 경우 ○ 아무런 코드 추가 없이 컴파일러가 알아서 자동 생성 ○ 클래스내 Move ctor / Move assign 선언에 = default; 사용하여 컴파일러가 구현부 생성 ○ 클래스내 Move ctor / Move assign 선언 및 구현부 추가 ● 컴파일러에 의한 이동 연산자에 대한 기본 함수 생성 조건 ○ 클래스에 Copy ctor / Copy assign 선언이 없어야 함 ○ 클래스에 Move ctor / Move assign 선언이 없어야 함 ○ 클래스에 소멸자 선언이 없어야 함 허용 금지
  • 25.
    4.2 Move Semantics(4/10) Default Function Creation (+11) Function(arguments) = default; 컴파일러가 기본 함수 생성하도록 명시 Function Suppression Function(arguments) = delete; 이동은 허용하지만 복사는 허용하지 않는다면, 하기 같은 매크로 이용하면 좋을 듯 #define DISALLOW_COPY_AND_ASSIGN(TypeName) TypeName(const TypeName&) = delete; TypeName& operator=(const TypeName&) = delete 허용 금지
  • 26.
    4.2 Move Semantics(5/10) Emplacement methods for containers (+11) emplace(), emplace_back(), emplace_front(), emplace_hint() 삽입 push_back 보다 생성삽입 emplace_back 가 성능이 좋을 수 있음. Emplacement 또다른 장점은 한번에 여러개 인수 전달 가능 void emplace_back(Args&&...); v.emplace_back(“a”, “b”, “c”); => push_back 은? v.push_back(“a”); v.push_back(“b”); v.push_back(“c”); 허용 금지
  • 27.
    4.2 Move Semantics(6/10) push_back vs emplace_back std::vector<std::string> v; v.push_back(“abc”); // 임시객체 String 생성 -> move 생성자 -> 임시객체 소멸 v.emplace_back(“abc”) // char* 그대로 전달 -> String 생성 삽입 String s(“abc”); v.push_back(s); // 성능 차이 없음 v.emplace_back(s); // 성능 차이 없음 허용 금지
  • 28.
    4.2 Move Semantics(7/10) noexcept Specifier (+11) void f() noexcept; compiler 에게 최적화할 수 있는 기회 제공 (호출 스택이 풀릴 여지가 없다라고 과정하기 때문) move constructor 에서 적극적 사용 권장 => std::vector::push_back 을 포함한 여러 표준 라이브러리 함수에서 move 연산이 수행됨 * move 연산자에 noexcept 명시자가 없으면, exception 이 발생할 수 있는 코드라 간주, 이전 상태로 돌릴 수 있는 복사 연산을 선택함. Chromium 사용 예제) GURL(GURL&& other) noexcept; GURL& operator=(GURL&& other) noexcept; 허용 금지
  • 29.
    4.2 Move Semantics(8/10) Move Iterator Adaptor (+11) std::make_move_iterator() 반복문을 통해 객체들을 copy 대신 move 처리 std::unique_ptr 같은 move only 타입들을 담고 있는 container 들 이동할 때 유용함 허용 금지
  • 30.
    4.2 Move Semantics(9/10) Move Iterator Adaptor(+11) 사용 예제 std::list<std::string> s{"one", "two", "three"}; std::vector<std::string> v1(s.begin(), s.end()); // copy std::vector<std::string> v2(std::make_move_iterator(s.begin()), std::make_move_iterator(s.end())); // move v1, v2 는 "one", "two", "three" s 는 empty 허용 금지
  • 31.
    4.2 Move Semantics(10/10) Ref-qualified Member Functions (+11) class T { void f() & {} void f() && {} }; Chromium 의 경우) 지나치게 모호한 성격을 띄기 때문에 OWNERS 의 승인 후 사용 가능 T t; t.f(); // first T().f(); // second std::move(t).f(); // second 허용 금지
  • 32.
    4.3 Type Deduction(1/3) Automatic Types (+11) auto a = 1 + 2; readability 를 고려해서 사용 - raw pointer 에서는 auto* 를 사용 (auto 가 smart pointer 인지 raw pointer 인지 알수가 없음) Trailing Return Types (+11) auto function declaration -> return_type readability 가 좋은 곳에서만 사용할 것 허용 금지
  • 33.
    4.3 Type Deduction(2/3) Declared Type Accessor (+11) decltype(expression) bool f(const Widget& w) auto w1 = w; // auto 는 Widget 로 deduction 됨 decltype(w) // decltype 은 const Widget& 으로 deduction 됨 허용 금지
  • 34.
    4.3 Type Deduction(3/3) Function return type deduction (+14) auto f() { return 42; } decltype(auto) g() { return 42; } Chromium 의 경우) abstract template code 주요 사용할 것을 권장 - decltype(auto) 는 템플릿된 forwarding/wrapper function 에서 사용 template <typename T> decltype(auto) Unwrap(T&& o) { return Unwrapper<T>::Unwrap(std::forward<T>(o)); } - 이외의 경우 auto 를 사용 template <typename F> auto BindLambdaForTesting(F&& f) 허용 금지
  • 35.
    4.4 Lambdas (1/5) Lambdaexpressions (+11) [captures](params) -> ret { body } default captures ([=], [&]) 에서 조심하게 사용해야함. Chromium 의 경우) 다음과 같이 제약함 - 스택 프레임 밖 lifecycle 를 가지는 로직에서는 어떤 capturing 값도 bind 하거나, store 하지 않아야 함 - 만약 캡쳐가 필요하면 base::Bind 로 감싸서 base::Callback을 등록하여 사용. - 캡쳐가 없더라도 base::Bind 와 함께 사용 가능 허용 금지
  • 36.
    4.4 Lambdas (2/5)- Chromium 사용 예제 • 캡쳐도 없고 스택 프레임 안에서 동작하는 경우 std::sort(v.begin(), v.end(), [](int x, int y) { return Weight(x) < Weight(y); }); • Lambda 를 base::Bind 로 감싼 경우 Return base::Bind( [](Status* status, bool accepted) { *status = accepted ? Status::ACCEPTED : Status::CANCELLED; }, status); • this 를 캡쳐해야하고 스택 프레임 밖에서(또는 다른 쓰레드에서) 동작하는 경우 io_task_runner_->PostTask( FROM_HERE, base::Bind(&ExternalDataUseObserver::OnDataUseBatchComplete, GetWeakPtr()));
  • 37.
    4.4 Lambdas (3/5) BindOperations std::bind(function, args, ...) 대신 base::Bind 사용 => Lifecyle 관리에 유용함 허용 금지 base::Bind(&Widget::Function, GetWeakPtr()); std::weak_ptr<Widget> thisWeakPtr(shared_from_this()); std::bind(&StaticFuntion, thisWeakPtr); staticFunction(std::weak_ptr<Widget> thisWeakPtr) { Widget w = thisWeakPtr.lock(); if (!w) return; w->Function(); }
  • 38.
    4.4 Lambdas (4/5) FunctionObjects std::function 대신 base::Callback 사용 - Class 의 refcounting 과 weak ptr 로 지원됨 허용 금지
  • 39.
    4.4 Lambdas (5/5) Genericlambdas (+14) [](const auto& x) { ... } 람다 argument type 을 위한 auto 사용 허용 금지
  • 40.
    4.5 Thread (1/3) threadLibraries (+11) <thread> <future> <mutex>, <condition_variable>.. Chromium 의 경우) - base::Thread 가 MessageLoop 와 이미 밀접하게 붙어있어 대체하기 어렵다 판단 - locking/synchronization 같은 기존 클래스를 표준 mutex, unique_lock 등으로 교체 가능한지 검토 예정 허용 금지
  • 41.
    4.5 Thread (2/3) Atomics(+11) std::atomic<types> for lockless concurrent programming 원자적 연산 가능 (RMW (Read/Modify/Write) 복사나 배정 불가능. => load() 와 store() 멤버 함수들 통해 배정 지원. memory-ordering 명시 가능. : load/store 에 명시된 메모리 순서에 따라, 쓰기 순서를 보장하거나, 쓰여지면 읽기를 수행하는 등 메모리 접근에 대한 순서를 명시할수 있음 허용 금지 Thread A Thread B Integer 10 read read 10 10 10 + 1 = 11 increment 10 + 1 = 11 increment 11 11 write write time
  • 42.
    4.5 Thread (3/3) thread_localstorage class (+11) thread_local int foo = 1; Chromium 의 경우) WTF::ThreadSpecific 을 사용했을 때 보다 성능 측면에서 2.5x 배 빠름 그러나 Android M 이전 버전에서 이상 동작. Mac 에서 TLS object 가 destroy 된 이후 재 접근시 새로 생성되는 이슈 있음. TLS object 가 POD type 이고 re-initialize 가 되어도 상관 없으면 사용해도 됨. Chromium 의 경우) /base/threading/sequence_local_storage_slot.h 사용 권장 허용 금지
  • 43.
    4.6 다루지 못한것들 1. 나머지 금지 항목들 / 허용하지만 조심히 다루어야 할 항목들 => Appendix 참조 2. 나머지 허용 항목들 / 아직 논의 중인 항목들 자세한 내용들은 https://chromium-cpp.appspot.com/ 참조
  • 44.
  • 45.
    5. Conclusion 1. ModernC++ 사용하자 - 성능 향상, 다양한 기능, 안정성, 간결한 코딩 2. 사용전에 지원 환경/동작 유무 등을 모두 검토하자 3. feature 특징에 따라 조심해서 사용하자 4. 내 프로젝트에도 C++ Coding Guide 를 세워보자 5. Chromium base library 참고해볼만하다
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
    A1. 이외 금지(1/8) Inline Namespaces (+11) namespace outer { inline namespace v2 { void Func() { return std::string("v2 Func"); } } namespace v1 { void Func() { return std::string("v1 Func"); } } } // namespace outer 인라인 네임스페이스의 멤버는 부모 네임스페이스의 멤버로 처리됨. 즉 outer::v2::Func 가 아니라 outer ::Func 로 직접 호출할 수 있음 큰 버전 정책에 용이하나, 불명확한 namespace 사용으로 혼란스러움. 허용 금지
  • 51.
    A1. 이외 금지(2/8) long long (+11) 64 bits integer third party code 와 호환하기 어려움 <stdint.h> 내에 int16_t, uint32_t, int64_t 같은 타입을 사용 허용 금지
  • 52.
    A1. 이외 금지(3/8) User-Defined Literals (+11) long double operator "" _w(long double); unsigned operator "" _w(const char*); 1.2_w; // calls operator "" _w(1.2L) 12_w; // calls operator "" _w("12") 구글 c++ style 가이드에서 금지 경험이 많은 C++ 개발자에게도 낯선 새로운 구문 형태는 혼란을 가중시킴 허용 금지
  • 53.
    A1. 이외 금지(4/8) Ratio Template Class (+11) std::ratio<numerator, denominator> std::cout << ratio_<1,2> + ratio_<1,3> << std::endl; Output : ⅚ google c++ style 에서 금지됨 해당 템플릿이 더 많은 템플릿 인터페이스들과 연결되어 있어 우려됨 허용 금지
  • 54.
    A1. 이외 금지(5/8) C Floating-Point Environment (+11) <cfenv>, <fenv.h> header 부동 소수점 상태 플래그 및 C 호환성을 위한 제어 모드 제공 google c++ style 가이드에서 금지됨 많은 컴파일러들이 제대로 지원하고 있지 않음 허용 금지
  • 55.
    A1. 이외 금지(6/8) Regular Expressions (+11) <regex> 별다른 논의는 없었음. Chromium내에 많은 regular expression libraries 가 기존재함으로 기존 것 사용. 혹은 thirdparty/re2 (by google) 사용 허용 금지
  • 56.
    A1. 이외 금지(7/8) std::chrono literals (+14) <chrono> header & std::chrono literals using namespace std::chrono_literals; auto timeout = 30s; Date and time utilities 별다른 논의는 없었으나 base/Time 유지하도록 함 허용 금지
  • 57.
    A1 이외 금지(8/8) Exceptions <exception> google c++ style 가이드에서 금지되어 있음 기존 코드에 적용하는 것보다 새로운 프로젝트에 사용하는게 적절함 exception 이 적용된 프로젝트를 기존 exception-free 코드에 적용하면 통합에 문제가 발생할 수 있음. 허용 금지
  • 58.
    A2. Alignment Alignment Features(+11) struct alignas(64) Empty64 {}; alignof(Empty64) => 64 alignas 는 export / #pragma pack 에서 호환되지 않음. - ALIGNAS() 대신 사용 (base/compiler_specific.h) Aligned storage (+11) std::aligned_storage<len, align> MSVC 2017 에서 align 8 byte 이상 값을 처리 못함 Alignment Features 대신 사용 std::aligned_storage<10, 64> => alignas(64) char data[10]; 허용 금지
  • 59.
    A3. Allow, ButBe careful Non-Static Class Member Initializers (+11) class C { type var = value; C() // copy-initializes var } 기본 사용 허용. 단, 하기 경우 제외 생성자 안에서 다른 멤버 초기화를 위해 필요한 경우 초기화가 복잡한 경우 허용 금지
  • 60.
    A3. Allow butBe careful Standard Integers (+11) Typedefs within <stdint.h> and <inttypes> int16_t, uint32_t, int64_t…. 등 size safe 한 타입 사용 권장 short, unsigned long long 도 허용, 단 size 에 대해 명확할 경우에만. unsigned 보다는 signed type 을 사용할 것 - unsigned 는 bit 표현을 위한 목적일 때만 사용 - negative 값이 절대 없다고 가정하여 unsigned를 사용하지는 말 것. signed 를 사용하되 Assertion으로 처리할 것 허용 금지
  • 61.
    A3. Allow butBe careful Raw string literals (+11) string var=R"(raw_string)"; GCC 4.9+ 보다 오래된 버전이라면, lexer(=lexical analyzer) bug 존재 => new line 이 포함된 literals 일 때 이슈 있음 #pragma omp parallel num_threads(sizeof R"( abc )") 허용 금지
  • 62.
    A3. Allow butBe careful Type Aliases ("using" instead of "typedef") (+11) using new_alias = typename typedef 대신 사용 C 와 호환되어야 할 헤더의 경우 제외 허용 금지
  • 63.
    A3. Allow butBe careful Constant Expressions (+11) constexpr int f(); constexpr bool b1 = noexcept(f()); Relaxed constant expressions (+14) constexpr 함수에서 return 이외 조건문 추가 등 및 복잡한 구현 가능 VS 2017 constexpr 함수내 loop 사용 지원됨 (17년 9월말) 변수에서는 const 보다 constexpr 를 사용, 함수에서는 조심스럽게 - 복잡성을 가지는 함수에서는 사용을 피할 것. - 함수를 inline 으로 강제하기 위한 목적으로 사용하지 말 것 허용 금지
  • 64.
    A3. Allow butBe careful Uniform Initialization Syntax (+11) type name {[value ..., value]}; 심플한 초기화를 위해서는 assignment syntax 사용 std::pair<bool, double> p = {true, 2.0}; std::vector<std::string> v = {"one", "two", "three"}; 특별한 로직, 명시적 생성자, reader 에게 직관적으로 심플하지 않을 때 constructor syntax 사용 MyClass c(1.7, false, "test"); std::vector<double> v(500, 0.97); // Creates 500 copies of the provided initializer uniform init syntax 사용. (= 없는 {} 사용) C c{true}; // Cannot use '=' since C() is explicit (and "()" is invalid syntax here) auto 와 섞어서 쓰지 말 것 - auto x{1} => 의도가 int 로 보이지만 std::initializer_list<int> 로 동작됨) 허용 금지