2017-10-13 14 views
0

매우 불행하게도 2 단계 초기화를 사용하는 class이 있습니다. 이 건설 된 후 초기화 메소드가 호출되지 않는 한, 개체가 여전히 사용할 준비가 없다는 것을 의미 :함수 내부에서 함수 반환 유형을 유추하십시오.

class A 
{ 
public: 
    A(); 
    bool init(); 

private: 
    bool m_is_initialized; 
}; 

이 정책을 따라야한다 클래스의 다른 모든 방법 : 방법이 때 클래스를 호출하는 경우 가 아직 초기화되지 않은 경우 메서드는 클래스 특정 채널에서 실행을 중지하고 오류를 기록해야합니다.

일부 메서드는 반환 형식이 있습니다. 이 경우 정책은 반환 값 형식의 기본 생성 값을 반환하는 것입니다.

아이디어는, 같은 모든 메소드 구현의 시작 부분에서 호출 할 수있는 쉬운 매크로를 가지고하는 것입니다,

#define CHECK_INITIALIZED             \ 
if (!m_is_initialized)             \ 
{                   \ 
    LOG_E(m_channel, "%s object not initialized.", __PRETTY_FUNCTION__); \ 
    assert(false);              \ 
    return;                \ 
} 

return 문, 그런데 만 void 기능 유효하며, 모든 경우에 맞지 않습니다. 매크로가 확장 된 함수의 반환 유형 T을 유추 할 수있는 방법이 있습니까? T()을 반환하고 매크로를 모든 곳에서 사용할 수있게 만들 수 있습니까?

편집 : 프로젝트 제한으로 인해 예외는 유감스럽게 사용할 수 없습니다.

+1

값 초기화 된 반환 값이 유효하지 않습니다. 일반적으로 일부 유형은 기본 구성 가능하지 않으며 다른 유형은 유효한 리턴 값입니다. 예를 들어, 함수는 성공한 반환 값으로 0 또는 ""등을 반환 할 수 있습니다. – chris

+4

'return' 대신'throw'를 쓰지 않는 이유는 무엇입니까? – NathanOliver

+0

@chris 로깅이 발생하는 한 우리는 그걸로 충분합니다. – nyarlathotep108

답변

0

return {};을 사용하면 기본 초기화 된 반환 유형을 나타낼 수 있습니다. 형식이 기본 생성 가능하지 않거나 참조를 반환하려고 시도하거나 반환 형식이 void 인 경우이 작업은 실패합니다.

또 다른 옵션은 return {};과 함께 모든 함수의 반환 형식으로 boost::optional/std:optional을 사용하는 것입니다. 이렇게하면 아무것도하지 않을 때 기본 옵션을 반환 할 수 있습니다 (따라서 옵션이 비어 있음).

또 다른 옵션은 매크로에 반환 값을 전달하고

#define CHECK_INITIALIZED(default_return_value)       \ 
if (!m_is_initialized)             \ 
{                   \ 
    LOG_E(m_channel, "%s object not initialized.", __PRETTY_FUNCTION__); \ 
    return default_return_value;                \ 
} 
+0

{}을 (를) 반환하겠습니까? 함수가 "반환"하면 컴파일합니까? – roalz

+0

@roalz, 아니요,하지만 당신은'std :: optional '또는'boost :: optional '를 사용하여 일관되게 만들 수 있습니다. – chris

+0

@roalz 아니, 그 대답을 업데이트했습니다. – NathanOliver

3

전혀 사용하는 이유 매크로 등의 반환을 위해 그것을 사용하는 것입니다? 템플릿이이 문제를 잘 처리 할 것입니다.

struct has_init_check 
{ 
protected: 
    template<class F> 
    auto if_ready(F&& f) 
    { 
     if (m_is_initialized) 
     { 
      f(); 
     } 
     else 
     { 
      // log the error here 
     } 
    } 

    void notify_initialized() 
    { 
     m_is_initialized = true; 
    } 

private: 
    bool m_is_initialized = false; 
}; 

class A 
: has_init_check 
{ 
public: 
    A(); 
    bool init() { notify_initialized(); } 

    int foo(); 
    void bar(); 

}; 

int A::foo() 
{ 
    int result = 0; // default value 
    if_ready([&] 
    { 
     // logic here 
     result = 10; 
    }); 

    return result;  
} 

void A::bar() 
{ 
    if_ready([] 
    { 
     // logic here 
    }); 
} 
+0

매크로는 주로 함수 이름을 기록하기 위해 사용됩니다. – nyarlathotep108

0

다른 대답, 다른 접근법.

예외는 허용되지 않지만 변형 (오류, 개체)을 사용하여 생성시 초기화 할 수 있습니다.

개체의 모든 구성원이 nothrow_constructible이 될 수 있음을 알고 있습니다 (제약 조건 임). 그러므로 그들은 또한 행동해야합니다.

그래서 우리는 변형과 선택의 조합을 사용하여 객체 생성을 관리하고 실패시에 로그/정지를 수행 할 수 있습니다.

이제 메소드를 구현할 때 런타임 오버 헤드가 없습니다.

#include <variant> 
#include <optional> 
#include <string> 
#include <cstdlib> 
#include <iostream> 
#include <type_traits> 


struct construction_error 
{ 
    construction_error(std::string s) 
    : message_(s) 
    {} 

    const std::string message() const { 
     return message_; 
    } 

    std::string message_; 
}; 

template<class T> using construction_result = std::variant<construction_error, T>; 

template<class T> struct tag {}; 

template<class T, class...Args> 
auto construct(tag<T>, Args&&...args) -> construction_result<T> 
{ 
    auto x = T(std::forward<Args>(args)...); 
    if (auto result = x.init()) 
    { 
     return std::move(result).value(); 
    } 
    else 
    { 
     return std::move(x); 
    } 
} 

class A 
{ 
public: 
    A() noexcept { std::cout << "speculative construction" << std::endl; } 
    std::optional<construction_error> init() noexcept { 
     if (rand() < RAND_MAX/2) 
     { 
      return construction_error("failed to construct an A"); 
     } 
     else 
     { 
      // complete the construction 
      return {}; 
     } 
    } 

    int foo(); 
    void bar(); 

}; 

int A::foo() 
{ 
    std::cout << __func__ << std::endl; 
    // logic here 
    return 10; 
} 

void A::bar() 
{ 
    std::cout << __func__ << std::endl; 
    // logic here 
} 

void do_thing(A a, A b, A c) 
{ 
    a.foo(); 
    b.foo(); 
    c.foo(); 
    a.bar(); 
    b.bar(); 
    c.bar(); 
} 

template<class T> 
void maybe_report_failure(const T&) 
{ 
} 

void maybe_report_failure(construction_error const& cf) 
{ 
    std::cout << "construction failure: " << cf.message() << std::endl; 
} 

int main() 
{ 

    for (int i = 0 ; i < 100 ; ++i) 
    { 
     auto maybe_a_1 = construct(tag<A>()); 
     auto maybe_a_2 = construct(tag<A>()); 
     auto maybe_a_3 = construct(tag<A>()); 

     auto action = [](auto&&...as) 
     { 
      constexpr bool good = (std::is_same_v<std::decay_t<decltype(as)>, A> && ...); 
      if constexpr (good) 
      { 
       do_thing(std::move(as)...); 
      } 
      else 
      { 
       (maybe_report_failure(as), ...); 
      } 
     }; 
     std::visit(action, 
      std::move(maybe_a_1), 
      std::move(maybe_a_2), 
      std::move(maybe_a_3)); 
    } 
} 

http://coliru.stacked-crooked.com/a/397427a89afa728a

0

이 무효, 기본 작도 모두 + 이동의 작동하고 있어야 참조 형식 (테스트하지 :)) : R 무효가 될 수

#define CHECK_INITIALIZED_WITH_RETURN(R)             \ 
if (!m_is_initialized)             \ 
{                   \ 
    LOG_E(m_channel, "%s object not initialized.", __PRETTY_FUNCTION__); \ 
    assert(false);              \ 
    static std::conditional_t<std::is_same_v<R,void>,int,std::decay_t<R>> some_default{}; \ 
    return R(some_default); \ 
} 

, T, T &, ... (기본 생성 된 정적 정적 효과가 없으며 정상적인 방법으로 사용된다고 가정) ...