<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>장형이의 일기</title>
    <link>https://developstudy.tistory.com/</link>
    <description>게임 서버로 먹고 사는 왕초보자.</description>
    <language>ko</language>
    <pubDate>Mon, 18 May 2026 22:00:47 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>장형이</managingEditor>
    <image>
      <title>장형이의 일기</title>
      <url>https://tistory1.daumcdn.net/tistory/2893567/attach/10ca1c8bb95c4082aa4a23fee0f54b61</url>
      <link>https://developstudy.tistory.com</link>
    </image>
    <item>
      <title>C++ 람다에서 클래스 멤버 변수를 복사로 캡쳐하여 사용하면 클래스 자체를 복사한다.</title>
      <link>https://developstudy.tistory.com/116</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;C++에서 람다로 복사 캡쳐 하여 클래스의 멤버 변수를 사용하면&lt;/b&gt; 멤버 변수만 복사되어 넘어오는 것이 아니고, &lt;b&gt;클래스 그 자체가 복사된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C#에서는 async의 매직 코드로 멤버 변수만 넘어왔었는데, 이 사실을 모르고 C++에서도 비슷하게 사용했다가 복사로 인한 부하가 심각하게 발생하는 문제가 발생하였었다. -_-;;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;코드&lt;/h2&gt;
&lt;pre id=&quot;code_1748856412380&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;

class CMember
{
public:
    CMember() { std::cout &amp;lt;&amp;lt; &quot;CMember 기본 생성자&quot; &amp;lt;&amp;lt; std::endl; }
    CMember(const CMember&amp;amp; _other) { std::cout &amp;lt;&amp;lt; &quot;CMember 복사 생성자&quot; &amp;lt;&amp;lt; std::endl; }
};

class CMain {
public:
    CMain() { std::cout &amp;lt;&amp;lt; &quot;CMain 기본 생성자&quot; &amp;lt;&amp;lt; std::endl; }
    CMain(const CMain&amp;amp; _other) { std::cout &amp;lt;&amp;lt; &quot;CMain 복사 생성자&quot; &amp;lt;&amp;lt; std::endl; }

    CMember m_Member;
};

void MyFunction(const CMain&amp;amp; _main) {}

int main(int argc, const char* argv[])
{
    CMain main_class;

    std::cout &amp;lt;&amp;lt; &quot;**** 람다 생성 ****&quot; &amp;lt;&amp;lt; std::endl;
    auto func = [=]() -&amp;gt; void { MyFunction(main_class); };

    return 0;
}

/*
CMember 기본 생성자
CMain 기본 생성자
**** 람다 생성 ****
CMember 기본 생성자
CMain 복사 생성자
*/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자 이렇게 main_class를 복사 캡쳐 모드에서 사용하면 main_class가 복사되어 복사 생성자가 당연히 호출되는 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 멤버 변수를 사용하면 어떨까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1748857493300&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;

class CMember
{
public:
    CMember() { std::cout &amp;lt;&amp;lt; &quot;CMember 기본 생성자&quot; &amp;lt;&amp;lt; std::endl; }
    CMember(const CMember&amp;amp; _other) { std::cout &amp;lt;&amp;lt; &quot;CMember 복사 생성자&quot; &amp;lt;&amp;lt; std::endl; }
};

class CMain {
public:
    CMain() { std::cout &amp;lt;&amp;lt; &quot;CMain 기본 생성자&quot; &amp;lt;&amp;lt; std::endl; }
    CMain(const CMain&amp;amp; _other) { std::cout &amp;lt;&amp;lt; &quot;CMain 복사 생성자&quot; &amp;lt;&amp;lt; std::endl; }

    CMember m_Member;
};

void MyFunction(const CMember&amp;amp; _member) {}

int main(int argc, const char* argv[])
{
    CMain main_class;

    std::cout &amp;lt;&amp;lt; &quot;**** 람다 생성 ****&quot; &amp;lt;&amp;lt; std::endl;
    auto func = [=]() -&amp;gt; void { MyFunction(main_class.m_Member); };

    return 0;
}
/*
CMember 기본 생성자
CMain 기본 생성자
**** 람다 생성 ****
CMember 기본 생성자
CMain 복사 생성자
*/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 멤버 변수를 사용하도록 바꿨음에도 불구하고 여전히 CMain이 복사되어 사용되는 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 거대 클래스의 멤버 변수를 복사 캡쳐 할 때에는 반드시 주의가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1748857597926&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;

class CMember
{
public:
    CMember() { std::cout &amp;lt;&amp;lt; &quot;CMember 기본 생성자&quot; &amp;lt;&amp;lt; std::endl; }
    CMember(const CMember&amp;amp; _other) { std::cout &amp;lt;&amp;lt; &quot;CMember 복사 생성자&quot; &amp;lt;&amp;lt; std::endl; }
};

class CMain {
public:
    CMain() { std::cout &amp;lt;&amp;lt; &quot;CMain 기본 생성자&quot; &amp;lt;&amp;lt; std::endl; }
    CMain(const CMain&amp;amp; _other) { std::cout &amp;lt;&amp;lt; &quot;CMain 복사 생성자&quot; &amp;lt;&amp;lt; std::endl; }

    CMember m_Member;
};

void MyFunction(const CMember&amp;amp; _member) {}

int main(int argc, const char* argv[])
{
    CMain main_class;

    std::cout &amp;lt;&amp;lt; &quot;**** 람다 생성 ****&quot; &amp;lt;&amp;lt; std::endl;
    CMember _member = main_class.m_Member;
    auto func = [=]() -&amp;gt; void { MyFunction(_member); };

    return 0;
}
/*
CMember 기본 생성자
CMain 기본 생성자
**** 람다 생성 ****
CMember 복사 생성자
CMember 복사 생성자
*/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CMain의 불필요한 복사를 막고 CMember만의 온전한 복사를 하려면 위와 같이 코드를 수정해야 할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;주절&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C++의 캡쳐에서는 모호한 표현(main_class를 복사할 것인가? m_Member만 복사할 것인가?, l, r value는 어떻게 할 것이지?)을 최소화하기 위해서 표현식(main_class.m_Meber)을 캡쳐하는 것을 금하고, 로컬 변수명(main_class)만을 캡쳐 가능하도록 디자인하였다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 멤버 변수를 복사 생략 캡쳐하여 사용하면 로컬 변수인 main_class를 캡쳐한다음 멤버 변수로 꺼내 쓰는 방식으로 동작하게 되는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 모호함이 다시 생겨나는데 실수하지 않도록 반드시 주의가 필요할 것 같다.&lt;/p&gt;</description>
      <category>Programming/C++ &amp;amp; Unreal</category>
      <category>C++</category>
      <category>람다</category>
      <category>캡쳐</category>
      <author>장형이</author>
      <guid isPermaLink="true">https://developstudy.tistory.com/116</guid>
      <comments>https://developstudy.tistory.com/116#entry116comment</comments>
      <pubDate>Mon, 2 Jun 2025 18:58:11 +0900</pubDate>
    </item>
    <item>
      <title>메모리 파편화 + boost, tbb 도구</title>
      <link>https://developstudy.tistory.com/115</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C++ 틱 베이스의 게임 서버에서 std::vector나 std::map을 매 초마다 수만, 수십만 회 생성하고 있는데 N시간이 지나면 이 코드에서 메모리 파편화(추정)가 심해져서 heap 할당 시에 CPU 사용량은 낮아지고 모든 스레드가 직렬화되는 문제가 확인되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 문제를 해결하기 위해서 pooling, 할당자 관련 도구를 찾다가 간단하게 정리해 보려고 포스팅하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;도구 한 줄 정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;boost object pool : 매우 큰 오브젝트 하나하나를 직접 다루기 좋은 도구.&lt;br /&gt;&lt;span style=&quot;text-align: start;&quot;&gt;boost pool_allocator : boost object pool 기반으로 할당해 주는 기능.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;text-align: start;&quot;&gt;boost fast_pool_allocator : boost object pool 기반으로 할당해 주는 기능. 단일 스레드 앱에서 최적화.&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;tbb scalable allocator : 멀티 쓰레드 환경에서 쓰기 좋은 할당자, heap 할당 / 해제에서 직렬화되지 않으며 lock-free 하게 작동, 스레드를 오가면서 오브젝트 주체가 변경되어도 문제없음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;boost object pool 사용법&lt;/h2&gt;
&lt;pre id=&quot;code_1743331971068&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;boost/pool/object_pool.hpp&amp;gt;

class Test {
public:
    Test() { std::cout &amp;lt;&amp;lt; &quot;Test object created\n&quot;; }
    ~Test() { std::cout &amp;lt;&amp;lt; &quot;Test object destroyed\n&quot;; }
};

int main() {
    boost::object_pool&amp;lt;Test&amp;gt; pool;

    Test* obj = pool.construct();
    pool.destroy(obj);

    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;할당자 계열(그 외)&lt;/h2&gt;
&lt;pre id=&quot;code_1743333052167&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;boost/pool/pool_alloc.hpp&amp;gt;
#include &amp;lt;tbb/scalable_allocator.h&amp;gt;

int main() {
    std::vector&amp;lt;int, boost::pool_allocator&amp;lt;int&amp;gt;&amp;gt; vec;
    // std::vector&amp;lt;int, boost::fast_pool_allocator&amp;lt;int&amp;gt;&amp;gt; vec;
    // std::vector&amp;lt;int, tbb::scalable_allocator&amp;lt;int&amp;gt;&amp;gt; vec;

    // 1000개의 정수 할당
    for (int i = 0; i &amp;lt; 1000; ++i) {
        vec.push_back(i);
    }

    // 벡터의 값을 출력
    for (const int&amp;amp; val : vec) {
        std::cout &amp;lt;&amp;lt; val &amp;lt;&amp;lt; &quot; &quot;;
    }
    std::cout &amp;lt;&amp;lt; std::endl;

    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결론&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 간단하게 한 줄 추가하는 것 만으로 메모리 파편화가 쉽게 해결되고 풀링을 지원할 수 있다니 최고다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 어쨌든 좋은 도구를 쓰더라도 저런 단순 컨테이너 따위로 파편화가 발생하는 것은 바람직하지 않으므로 코드 정리도 반드시 하는 게 좋을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리 프로젝트에서도 문제 클래스의 코드 정리와 tbb를 같이 적용해 두니 문제가 더 이상 문제가 발생되지 않게 개선되었는데 제발 더 이상 문제가 없기를 바란다.  &lt;/p&gt;</description>
      <category>Programming/C++ &amp;amp; Unreal</category>
      <category>allocator</category>
      <category>Boost</category>
      <category>C++</category>
      <category>object_pool</category>
      <category>TBB</category>
      <category>할당자</category>
      <author>장형이</author>
      <guid isPermaLink="true">https://developstudy.tistory.com/115</guid>
      <comments>https://developstudy.tistory.com/115#entry115comment</comments>
      <pubDate>Sun, 30 Mar 2025 20:13:34 +0900</pubDate>
    </item>
    <item>
      <title>C++ 스마트 포인터 메모리는 shared_ptr가 없어져도 해제되지 않는다.</title>
      <link>https://developstudy.tistory.com/114</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C++ 스마트 포인터 shared_ptr의 참조 카운트가 0이 되어도 weak_ptr이 참조하고 있다면 메모리를 해제하지 않고 소멸자만 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;스마트 포인터 간단 요약&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1536&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7cZ0v/btsM1csKQLR/eURwZp4nLOs7Q0ov5k9d5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7cZ0v/btsM1csKQLR/eURwZp4nLOs7Q0ov5k9d5k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7cZ0v/btsM1csKQLR/eURwZp4nLOs7Q0ov5k9d5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7cZ0v%2FbtsM1csKQLR%2FeURwZp4nLOs7Q0ov5k9d5k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;302&quot; height=&quot;453&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1536&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;스마트 포인터의 기본 원리&lt;/b&gt;는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;make_shared시에, control block&lt;/b&gt;(스마트 포인터 관리용 오브젝트)&lt;b&gt;와 object&lt;/b&gt;(실제 사용하는 오브젝트)가 &lt;b&gt;생성&lt;/b&gt;된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 &lt;b&gt;shared_ptr이 참조&lt;/b&gt;할 때마다 &lt;b&gt;use_count&lt;/b&gt;가 늘어나고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;weak_ptr이 참조&lt;/b&gt;할 때 마다 &lt;b&gt;weak_count&lt;/b&gt;가 늘어난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;use_count가 0&lt;/b&gt;이 되면 &lt;b&gt;object의 소멸자가 호출&lt;/b&gt;되고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;모든 count가 0&lt;/b&gt;이 되면 &lt;b&gt;control block과 object의 메모리가 해제&lt;/b&gt;된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;메모리 해제 시점&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상식적으로 use_count가 0이 될 때, object의 메모리를 해제해야 하는 것 아닌가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 그렇지 않다. 실험을 해보자.&lt;/p&gt;
&lt;pre id=&quot;code_1743329761713&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;memory&amp;gt;
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;string&amp;gt;

class A{
public:
    int number;
};

int main(int argc, const char * argv[])
{
    A* ptr_a = nullptr;
    // std::weak_ptr&amp;lt;A&amp;gt; weak_ptr_a;
    
    {
        std::shared_ptr&amp;lt;A&amp;gt; shared_ptr_a = std::make_shared&amp;lt;A&amp;gt;();
        shared_ptr_a-&amp;gt;number = 500;
        ptr_a = shared_ptr_a.get();
        // weak_ptr_a = shared_ptr_a;
    }
    
    // 주석을 설정하면 My number is 0
    // 주석을 해제하면 My number is 500
    std::cout &amp;lt;&amp;lt; &quot;My number is &quot; &amp;lt;&amp;lt; ptr_a-&amp;gt;number &amp;lt;&amp;lt; std::endl;
    
    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 weak_ptr 유무에 따라서 결과가 달라지는 것을 볼 수있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1714&quot; data-origin-height=&quot;162&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lm6Si/btsM2dYHqX1/kAWOTPZkUNIXWSDc7cmtJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lm6Si/btsM2dYHqX1/kAWOTPZkUNIXWSDc7cmtJ1/img.png&quot; data-alt=&quot;주석 설정시 vs memory debug&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lm6Si/btsM2dYHqX1/kAWOTPZkUNIXWSDc7cmtJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Flm6Si%2FbtsM2dYHqX1%2FkAWOTPZkUNIXWSDc7cmtJ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1714&quot; height=&quot;162&quot; data-origin-width=&quot;1714&quot; data-origin-height=&quot;162&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;주석 설정시 vs memory debug&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1734&quot; data-origin-height=&quot;160&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3omyx/btsM3jcReiZ/t56iuKi2nLO02KQ7ARVF4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3omyx/btsM3jcReiZ/t56iuKi2nLO02KQ7ARVF4K/img.png&quot; data-alt=&quot;주석 해제시 memory debug&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3omyx/btsM3jcReiZ/t56iuKi2nLO02KQ7ARVF4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3omyx%2FbtsM3jcReiZ%2Ft56iuKi2nLO02KQ7ARVF4K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1734&quot; height=&quot;160&quot; data-origin-width=&quot;1734&quot; data-origin-height=&quot;160&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;주석 해제시 memory debug&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 visual studio memory debugging에서도 메모리 사용 여부가 잡힌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;코드 검증&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;make_shared 단&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2060&quot; data-origin-height=&quot;782&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3hxtd/btsM1fDfwyv/sfK6m9pC1K5D1k7Yk3xkz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3hxtd/btsM1fDfwyv/sfK6m9pC1K5D1k7Yk3xkz1/img.png&quot; data-alt=&quot;make_shared시에 생성되는 객체&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3hxtd/btsM1fDfwyv/sfK6m9pC1K5D1k7Yk3xkz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3hxtd%2FbtsM1fDfwyv%2FsfK6m9pC1K5D1k7Yk3xkz1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2060&quot; height=&quot;782&quot; data-origin-width=&quot;2060&quot; data-origin-height=&quot;782&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;make_shared시에 생성되는 객체&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 make_shared시에 생성되는 _Ref_count_obj2에는 _Ty를 포인터가 아닌 멤버 변수로 바로 만들고 있어서 따로 해제할 수 있는 방법이 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;use count 감소 처리단&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1128&quot; data-origin-height=&quot;328&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Bh8sA/btsM05Vgf8I/Tq9GaxSF3R7wIXN6xwrEQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Bh8sA/btsM05Vgf8I/Tq9GaxSF3R7wIXN6xwrEQK/img.png&quot; data-alt=&quot;use count가 줄어 들 경우 호출되는 함수&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Bh8sA/btsM05Vgf8I/Tq9GaxSF3R7wIXN6xwrEQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBh8sA%2FbtsM05Vgf8I%2FTq9GaxSF3R7wIXN6xwrEQK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;485&quot; height=&quot;141&quot; data-origin-width=&quot;1128&quot; data-origin-height=&quot;328&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;use count가 줄어 들 경우 호출되는 함수&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1462&quot; data-origin-height=&quot;164&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/q5YTS/btsMZJEBN6E/8CwfjXDedaMK5Sd3sVYmkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/q5YTS/btsMZJEBN6E/8CwfjXDedaMK5Sd3sVYmkk/img.png&quot; data-alt=&quot;위 함수에서 호출되는 함수&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/q5YTS/btsMZJEBN6E/8CwfjXDedaMK5Sd3sVYmkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fq5YTS%2FbtsMZJEBN6E%2F8CwfjXDedaMK5Sd3sVYmkk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;430&quot; height=&quot;48&quot; data-origin-width=&quot;1462&quot; data-origin-height=&quot;164&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;위 함수에서 호출되는 함수&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1338&quot; data-origin-height=&quot;438&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xEwxM/btsMZ2K9maY/OjHWKiyPXGIyzk3SBKgDRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xEwxM/btsMZ2K9maY/OjHWKiyPXGIyzk3SBKgDRk/img.png&quot; data-alt=&quot;위 함수에서 호출되는 함수&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xEwxM/btsMZ2K9maY/OjHWKiyPXGIyzk3SBKgDRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxEwxM%2FbtsMZ2K9maY%2FOjHWKiyPXGIyzk3SBKgDRk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;424&quot; height=&quot;139&quot; data-origin-width=&quot;1338&quot; data-origin-height=&quot;438&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;위 함수에서 호출되는 함수&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드를 근거로 use count가 0이되어도 _Obj.~_Ty() 즉 소멸자 호출만 일어나고 메모리 해제는 따로 일어나지 않는 것을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;weak count 감소 처리단&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1372&quot; data-origin-height=&quot;276&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rAgsC/btsM2y2BRoa/dPyBDq1y0nJtz5kIPPuzWk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rAgsC/btsM2y2BRoa/dPyBDq1y0nJtz5kIPPuzWk/img.png&quot; data-alt=&quot;weak count 감소시에 호출되는 함수&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rAgsC/btsM2y2BRoa/dPyBDq1y0nJtz5kIPPuzWk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrAgsC%2FbtsM2y2BRoa%2FdPyBDq1y0nJtz5kIPPuzWk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;452&quot; height=&quot;91&quot; data-origin-width=&quot;1372&quot; data-origin-height=&quot;276&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;weak count 감소시에 호출되는 함수&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;184&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dMDT6w/btsMZ5uljEc/jj66WXx66JwrjjB0mKgzhk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dMDT6w/btsMZ5uljEc/jj66WXx66JwrjjB0mKgzhk/img.png&quot; data-alt=&quot;위 함수에서 호출되는 함수&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dMDT6w/btsMZ5uljEc/jj66WXx66JwrjjB0mKgzhk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdMDT6w%2FbtsMZ5uljEc%2Fjj66WXx66JwrjjB0mKgzhk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;472&quot; height=&quot;68&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;184&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;위 함수에서 호출되는 함수&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;weak count가 0이 될 경우 컨트롤 블록이 delete 되며 멤버 변수로 생성되어 있는 object도 같이 해제되는 모습이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;왜 그럴까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 저렇게 설계하였는지 명확한 이유는 찾기 힘들지만, &lt;b&gt;Windows/MacOS에서는 그렇게 작동하는 것을 확인&lt;/b&gt;하였으며 &lt;b&gt;control block과 object를 같은 메모리 위치에 할당&lt;/b&gt;하고 &lt;b&gt;두 객체를 항상 동시에 생성 및 해제&lt;/b&gt;시키려고 그렇게 만든 것으로 추정된다.&lt;/p&gt;</description>
      <category>Programming/C++ &amp;amp; Unreal</category>
      <category>C++</category>
      <category>메모리해제</category>
      <category>스마트포인터</category>
      <author>장형이</author>
      <guid isPermaLink="true">https://developstudy.tistory.com/114</guid>
      <comments>https://developstudy.tistory.com/114#entry114comment</comments>
      <pubDate>Sun, 30 Mar 2025 19:41:33 +0900</pubDate>
    </item>
    <item>
      <title>vprintf, vsprintf 등 가변 인자 함수를 활용한 로그 함수 개발 시에 포맷 warning 출력하기</title>
      <link>https://developstudy.tistory.com/113</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;printf&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C++에서 printf 사용시에 format을 잘못 지정하였다면 이렇게 컴파일러단에서 C4477 C4313 C6067 의 워링 에러가 발생하여 잘못된 포맷으로 인한 오류나 크래시를 방지 할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;582&quot; data-origin-height=&quot;530&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c7MazM/btsGo6zHMDG/BH1OZSJemhAmFHILhKnDkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c7MazM/btsGo6zHMDG/BH1OZSJemhAmFHILhKnDkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c7MazM/btsGo6zHMDG/BH1OZSJemhAmFHILhKnDkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc7MazM%2FbtsGo6zHMDG%2FBH1OZSJemhAmFHILhKnDkK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;451&quot; height=&quot;411&quot; data-origin-width=&quot;582&quot; data-origin-height=&quot;530&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;vprintf, vsprintf...&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 보통 printf를 저렇게 사용하는 일은 거의 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 보통 로그를 남기는 함수를 va_list를 사용하여 커스터마이징하곤 한다. (UE_LOG도..)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이렇게 사용하면&lt;b&gt; 포맷 관련 warning이 발생하지 않아서 실수를 저지르기가 너무 쉽다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;863&quot; data-origin-height=&quot;835&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rmzuv/btsGqOYx10n/0kguS8k8GjNGrfyULYPink/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rmzuv/btsGqOYx10n/0kguS8k8GjNGrfyULYPink/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rmzuv/btsGqOYx10n/0kguS8k8GjNGrfyULYPink/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Frmzuv%2FbtsGqOYx10n%2F0kguS8k8GjNGrfyULYPink%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;562&quot; height=&quot;544&quot; data-origin-width=&quot;863&quot; data-origin-height=&quot;835&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MSVS에서는 저런 사용자 함수에서 포맷 체크를 하는 방법이 없어서 이렇게 편법으로 Debug 빌드 시에만 printf로 체크하는 방식이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sizeof() 안에 있는 printf구문은 런타임에 평가되지 않으므로 문제가 발생하지 않으니 걱정하지 않아도 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1712324052874&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdarg.h&amp;gt;

void Log(const char* szFormat, ...)
{
    va_list lpStart;
    va_start(lpStart, szFormat);

    vprintf(szFormat, lpStart);

    va_end(lpStart);
}

#ifdef _DEBUG
#define CHECK_FORMAT(...)                               \
	do {                                                \
		char const dummy = sizeof(printf(__VA_ARGS__)); \
		(void)dummy;                                    \
	} while (false)
#else
#define CHECK_FORMAT(...) __noop
#endif

#define LOG(...) { CHECK_FORMAT(__VA_ARGS__); Log(__VA_ARGS__); }

int main() {
    int a = 5;
    LOG(&quot;a : %s&quot;, a);
    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;844&quot; data-origin-height=&quot;714&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNC76P/btsGpMgtdTf/atzsRk6d5QJ13INzdSOD2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNC76P/btsGpMgtdTf/atzsRk6d5QJ13INzdSOD2k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNC76P/btsGpMgtdTf/atzsRk6d5QJ13INzdSOD2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNC76P%2FbtsGpMgtdTf%2FatzsRk6d5QJ13INzdSOD2k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;770&quot; height=&quot;651&quot; data-origin-width=&quot;844&quot; data-origin-height=&quot;714&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;GCC?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방법은 MSVC에서만 쓰는 방식이고 GCC에서는 attribute라는 구문으로 포맷 체크를 할 수 있다고 하니 참고 바란다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;attribute&lt;/b&gt;&lt;span style=&quot;background-color: #faf9f8; color: #000000; text-align: start;&quot;&gt;((format(printf, formatStringIndex, formatArgStartIndex))).&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관련 내용 : &lt;a href=&quot;https://developercommunity.visualstudio.com/t/support-format-specifier-checking-on-user-defined/843652&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developercommunity.visualstudio.com/t/support-format-specifier-checking-on-user-defined/843652&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1712323936432&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;&amp;lt;p&amp;gt;Support format specifier checking on user-defined fu...&quot; data-og-description=&quot;&amp;lt;p&amp;gt;As per: https://devblogs.microsoft.com/cppblog/format-specifiers-checking/&amp;lt;/p&amp;gt; &amp;lt;p&amp;gt;I have had a request for formal support of the checking of format...&quot; data-og-host=&quot;developercommunity.visualstudio.com&quot; data-og-source-url=&quot;https://developercommunity.visualstudio.com/t/support-format-specifier-checking-on-user-defined/843652&quot; data-og-url=&quot;https://developercommunity.visualstudio.com/t/support-format-specifier-checking-on-user-defined/843652&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/TuRxE/hyVJ1CeEok/PGq2TVKpQeMHGi3ctcRQS0/img.png?width=360&amp;amp;height=360&amp;amp;face=0_0_360_360&quot;&gt;&lt;a href=&quot;https://developercommunity.visualstudio.com/t/support-format-specifier-checking-on-user-defined/843652&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developercommunity.visualstudio.com/t/support-format-specifier-checking-on-user-defined/843652&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/TuRxE/hyVJ1CeEok/PGq2TVKpQeMHGi3ctcRQS0/img.png?width=360&amp;amp;height=360&amp;amp;face=0_0_360_360');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;&amp;lt;p&amp;gt;Support format specifier checking on user-defined fu...&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;lt;p&amp;gt;As per: https://devblogs.microsoft.com/cppblog/format-specifiers-checking/&amp;lt;/p&amp;gt; &amp;lt;p&amp;gt;I have had a request for formal support of the checking of format...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developercommunity.visualstudio.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Programming/C++ &amp;amp; Unreal</category>
      <category>C++</category>
      <category>C4313</category>
      <category>C4477</category>
      <category>C6067</category>
      <category>custom printf 함수</category>
      <category>vprintf</category>
      <category>vsprintf</category>
      <author>장형이</author>
      <guid isPermaLink="true">https://developstudy.tistory.com/113</guid>
      <comments>https://developstudy.tistory.com/113#entry113comment</comments>
      <pubDate>Fri, 5 Apr 2024 22:33:40 +0900</pubDate>
    </item>
    <item>
      <title>StackWalk64가 context에 의하여 Access violation를 유발</title>
      <link>https://developstudy.tistory.com/112</link>
      <description>&lt;pre id=&quot;code_1698002055946&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;windows.h&amp;gt;
#include &amp;lt;dbghelp.h&amp;gt;

BOOL InitSymHandler(HANDLE hProcess) {
    if (SymInitialize(hProcess, NULL, TRUE)) {
        SymSetOptions(SYMOPT_LOAD_LINES);
        return TRUE;
    }
    return FALSE;
}

LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exceptionInfo) {
    CONTEXT* context = exceptionInfo-&amp;gt;ContextRecord;
    STACKFRAME64 stackFrame;
    memset(&amp;amp;stackFrame, 0, sizeof(STACKFRAME64));

    stackFrame.AddrPC.Mode = AddrModeFlat;
    stackFrame.AddrPC.Offset = context-&amp;gt;Rip;
    stackFrame.AddrStack.Mode = AddrModeFlat;
    stackFrame.AddrStack.Offset = context-&amp;gt;Rsp;
    stackFrame.AddrFrame.Mode = AddrModeFlat;
    stackFrame.AddrFrame.Offset = context-&amp;gt;Rbp;

    while (StackWalk64(
        IMAGE_FILE_MACHINE_AMD64,
        GetCurrentProcess(),
        GetCurrentThread(),
        &amp;amp;stackFrame,
        context,
        NULL,
        SymFunctionTableAccess64,
        SymGetModuleBase64,
        NULL)) {
        DWORD64 address = stackFrame.AddrPC.Offset;
        CHAR buffer[sizeof(SYMBOL_INFO) + 256] = { 0 };
        PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer;
        pSymbol-&amp;gt;SizeOfStruct = sizeof(SYMBOL_INFO);
        pSymbol-&amp;gt;MaxNameLen = 255;

        if (SymFromAddr(GetCurrentProcess(), address, NULL, pSymbol)) {
            std::string functionName = pSymbol-&amp;gt;Name;
            std::cout &amp;lt;&amp;lt; &quot;Function: &quot; &amp;lt;&amp;lt; functionName &amp;lt;&amp;lt; &quot; at 0x&quot; &amp;lt;&amp;lt; std::hex &amp;lt;&amp;lt; address &amp;lt;&amp;lt; std::endl;
        }
        else {
            std::cout &amp;lt;&amp;lt; &quot;SymFromAddr failed, error: &quot; &amp;lt;&amp;lt; GetLastError() &amp;lt;&amp;lt; std::endl;
        }
    }

    return EXCEPTION_CONTINUE_SEARCH;
}

int main()
{
    HANDLE hProcess = GetCurrentProcess();
    InitSymHandler(hProcess);
	SetUnhandledExceptionFilter(UnhandledExceptionHandler);

    char* a = 0;
    *a = 5;
}

/*
출력 :
Function: main at 0x7ff6bd774587
Function: invoke_main at 0x7ff6bd7768f9
Function: __scrt_common_main_seh at 0x7ff6bd77679e
Function: __scrt_common_main at 0x7ff6bd77665e
Function: mainCRTStartup at 0x7ff6bd77698e
Function: BaseThreadInitThunk at 0x7ff8c79b7344
Function: RtlUserThreadStart at 0x7ff8c7b026b1
*/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;흔히 사용하는 StackWalk64를 사용하여 stackTrace를 찍는 예제.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 함수를 그대로 써서 현재 StackTrace를 찍어보고 싶어 개조해보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1698002785404&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;LONG WINAPI ExceptionHandler(EXCEPTION_POINTERS* exceptionInfo) {
    CONTEXT* context = exceptionInfo-&amp;gt;ContextRecord;
    STACKFRAME64 stackFrame;
    memset(&amp;amp;stackFrame, 0, sizeof(STACKFRAME64));

    stackFrame.AddrPC.Mode = AddrModeFlat;
    stackFrame.AddrPC.Offset = context-&amp;gt;Rip;
    stackFrame.AddrStack.Mode = AddrModeFlat;
    stackFrame.AddrStack.Offset = context-&amp;gt;Rsp;
    stackFrame.AddrFrame.Mode = AddrModeFlat;
    stackFrame.AddrFrame.Offset = context-&amp;gt;Rbp;

    while (StackWalk64(
        IMAGE_FILE_MACHINE_AMD64,
        GetCurrentProcess(),
        GetCurrentThread(),
        &amp;amp;stackFrame,
        context,
        NULL,
        SymFunctionTableAccess64,
        SymGetModuleBase64,
        NULL)) {
        DWORD64 address = stackFrame.AddrPC.Offset;
        CHAR buffer[sizeof(SYMBOL_INFO) + 256] = { 0 };
        PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer;
        pSymbol-&amp;gt;SizeOfStruct = sizeof(SYMBOL_INFO);
        pSymbol-&amp;gt;MaxNameLen = 255;

        if (SymFromAddr(GetCurrentProcess(), address, NULL, pSymbol)) {
            std::string functionName = pSymbol-&amp;gt;Name;
            std::cout &amp;lt;&amp;lt; &quot;Function: &quot; &amp;lt;&amp;lt; functionName &amp;lt;&amp;lt; &quot; at 0x&quot; &amp;lt;&amp;lt; std::hex &amp;lt;&amp;lt; address &amp;lt;&amp;lt; std::endl;
        }
        else {
            std::cout &amp;lt;&amp;lt; &quot;SymFromAddr failed, error: &quot; &amp;lt;&amp;lt; GetLastError() &amp;lt;&amp;lt; std::endl;
        }
    }

    return EXCEPTION_EXECUTE_HANDLER;
}

void PrintStackTrace() {
    __try {
        RaiseException(1, 0, 0, NULL);
    } // StackTrace 출력 후 여기서 Access violation이 발생한다.
    __except (ExceptionHandler(GetExceptionInformation())) {
    }
}

int main()
{
    HANDLE hProcess = GetCurrentProcess();
    InitSymHandler(hProcess);

    PrintStackTrace();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 강제로 Exception을 만들어서 StackTrace만 찍고 돌아가려고 했는데, stack unwind하는 과정에서 Access violation exception이 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1698002941298&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;LONG WINAPI ExceptionHandler(EXCEPTION_POINTERS* exceptionInfo) {
    CONTEXT context;
    memcpy(&amp;amp;context, exceptionInfo-&amp;gt;ContextRecord, sizeof(CONTEXT));


    STACKFRAME64 stackFrame;
    memset(&amp;amp;stackFrame, 0, sizeof(STACKFRAME64));

    stackFrame.AddrPC.Mode = AddrModeFlat;
    stackFrame.AddrPC.Offset = context.Rip;
    stackFrame.AddrStack.Mode = AddrModeFlat;
    stackFrame.AddrStack.Offset = context.Rsp;
    stackFrame.AddrFrame.Mode = AddrModeFlat;
    stackFrame.AddrFrame.Offset = context.Rbp;

    while (StackWalk64(
        IMAGE_FILE_MACHINE_AMD64,
        GetCurrentProcess(),
        GetCurrentThread(),
        &amp;amp;stackFrame,
        &amp;amp;context,
        NULL,
        SymFunctionTableAccess64,
        SymGetModuleBase64,
        NULL)) {
        DWORD64 address = stackFrame.AddrPC.Offset;
        CHAR buffer[sizeof(SYMBOL_INFO) + 256] = { 0 };
        PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer;
        pSymbol-&amp;gt;SizeOfStruct = sizeof(SYMBOL_INFO);
        pSymbol-&amp;gt;MaxNameLen = 255;

        if (SymFromAddr(GetCurrentProcess(), address, NULL, pSymbol)) {
            std::string functionName = pSymbol-&amp;gt;Name;
            std::cout &amp;lt;&amp;lt; &quot;Function: &quot; &amp;lt;&amp;lt; functionName &amp;lt;&amp;lt; &quot; at 0x&quot; &amp;lt;&amp;lt; std::hex &amp;lt;&amp;lt; address &amp;lt;&amp;lt; std::endl;
        }
        else {
            std::cout &amp;lt;&amp;lt; &quot;SymFromAddr failed, error: &quot; &amp;lt;&amp;lt; GetLastError() &amp;lt;&amp;lt; std::endl;
        }
    }

    return EXCEPTION_EXECUTE_HANDLER;
}

void PrintStackTrace() {
    __try {
        RaiseException(1, 0, 0, NULL);
    }
    __except (ExceptionHandler(GetExceptionInformation())) {
    }
}

int main()
{
    HANDLE hProcess = GetCurrentProcess();
    InitSymHandler(hProcess);

    PrintStackTrace();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 Context를 copy하여 사용하면 문제가 발생하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;StackWalk64 함수가 Context를 훼손시키는데, StackTrace 출력 이후 로직에서 ContextRecord를 다시 사용하기 때문에 Exception이 발생하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어찌되었건 이런 방식은 너무 이상하고 느리기 때문에 boost::stacktrace나 더 좋은 도구들을 사용하여서 stackTrace를 출력하는 것이 좋을 것 같다.&lt;/p&gt;
&lt;div id=&quot;gtx-trans&quot; style=&quot;position: absolute; left: 524px; top: 4891.75px;&quot;&gt;
&lt;div class=&quot;gtx-trans-icon&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;</description>
      <category>Programming/C++ &amp;amp; Unreal</category>
      <category>Access Violation</category>
      <category>C++</category>
      <category>context</category>
      <category>StackWalk64</category>
      <author>장형이</author>
      <guid isPermaLink="true">https://developstudy.tistory.com/112</guid>
      <comments>https://developstudy.tistory.com/112#entry112comment</comments>
      <pubDate>Mon, 23 Oct 2023 04:49:30 +0900</pubDate>
    </item>
    <item>
      <title>C++ 가변인자(va_list)를 오버로딩 하지 말자.</title>
      <link>https://developstudy.tistory.com/111</link>
      <description>&lt;pre id=&quot;code_1698000561779&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;stdarg.h&amp;gt;

void Print(const char* format, va_list args) { // 2번째 Print 함수에서 호출함
	vprintf(format, args);
}

void Print(const char* format, ...) { // main 에서 호출함
	va_list args;
	va_start(args, format);
	Print(format, args);
	va_end(args);
}

int main()
{
	char a[] = &quot;Hello &quot;;
	char b[] = &quot;world!&quot;;

	Print(&quot;%s %s&quot;, a, b); // &quot;Hello world!&quot; 출력됨.
	return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드는 아무 문제가 없다. 가변인자가 잘 들어가서 2번째 Print 함수 -&amp;gt; 1번째 Print를 호출한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1698000631234&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;stdarg.h&amp;gt;

void Print(const char* format, va_list args) { // main에서 호출됨.
	vprintf(format, args);
}

void Print(const char* format, ...) { // 호출되지 않음.
	va_list args;
	va_start(args, format);
	Print(format, args);
	va_end(args);
}

int main()
{
	char a[] = &quot;Hello &quot;;

	Print(&quot;%s&quot;, a); // Access violation 발생.
	return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드는 Access violation을 발생시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;va_list는 내부적으로 char* 이기 때문에, a가 va_list로 취급되어 1번째 Print 함수가 실행되고 Exception이 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경우에 따라서는 CallStack이 망가져서 디버깅하기 매우 힘들어지니 꼭 절대 가변인자 함수는 오버로딩 하지말고 얌전히 함수명을 분리하자. -_-;;&lt;/p&gt;</description>
      <category>Programming/C++ &amp;amp; Unreal</category>
      <category>Access Violation</category>
      <category>C++</category>
      <category>CPP</category>
      <category>exception</category>
      <category>overloading</category>
      <category>va_list</category>
      <category>가변인자</category>
      <category>예외</category>
      <category>오버로딩</category>
      <author>장형이</author>
      <guid isPermaLink="true">https://developstudy.tistory.com/111</guid>
      <comments>https://developstudy.tistory.com/111#entry111comment</comments>
      <pubDate>Mon, 23 Oct 2023 03:59:32 +0900</pubDate>
    </item>
    <item>
      <title>C++ parallel for_each (thread) exception</title>
      <link>https://developstudy.tistory.com/110</link>
      <description>&lt;pre id=&quot;code_1697999021868&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;windows.h&amp;gt;
#include &amp;lt;thread&amp;gt;
#include &amp;lt;execution&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;

LONG WINAPI UnhandleExceptionHandler(_EXCEPTION_POINTERS* exceptionInfo) {
	std::cout &amp;lt;&amp;lt; &quot;Exception catched!&quot;; // 출력되지 않음.
	return 0;
}

int main()
{
	SetUnhandledExceptionFilter(UnhandleExceptionHandler);

	// 쓰레드에서 std::Exception 발생.
	std::thread th1([]() { throw std::exception(); });
	th1.join();

	// parallel for_each에서 std::Exception 발생.
	std::vector&amp;lt;int&amp;gt; vec{ 1,2,3 };
	std::for_each(std::execution::par_unseq, vec.begin(), vec.end(), [](const int&amp;amp; num) {
		throw std::exception();
		});

	std::cout &amp;lt;&amp;lt; &quot;fin.&quot;; // 출력되지 않음.
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;thread나 for_each는 기본적으로 noexcept로 작동하기 때문에, C++ exception이 발생하면 std::terminate 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 위 코드에서 예외를 잡을 수 없고 아무런 출력도 발생하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1697999171181&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void TerminateHandler() {
	std::cout &amp;lt;&amp;lt; &quot;Program terminated!&quot;; // 출력됨.
}

int main()
{
	// 쓰레드에서 std::Exception 발생.
	std::thread th1([]() {
		std::set_terminate(TerminateHandler); throw std::exception();
		});
	th1.join();

	// parallel for_each에서 std::Exception 발생.
	std::vector&amp;lt;int&amp;gt; vec{ 1,2,3 };
	std::for_each(std::execution::par_unseq, vec.begin(), vec.end(), [](const int&amp;amp; num) {
		std::set_terminate(TerminateHandler);
		throw std::exception();
	});
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 위와 같이 TerminateHandler를 통하여 로그를 남기거나,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Thread,Task 관련 로직에 try_catch를 잘 심어두어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1697999670922&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;LONG WINAPI UnhandleExceptionHandler(_EXCEPTION_POINTERS* exceptionInfo) {
	std::cout &amp;lt;&amp;lt; &quot;Exception catched!&quot;; // 출력됨.
	return 0;
}

int main()
{
	SetUnhandledExceptionFilter(UnhandleExceptionHandler);

	// 쓰레드에서 SEH Exception 발생.
	std::thread th1([]() { 
		char* a = 0; *a = 5; 
	});
	th1.join();

	// parallel for_each에서 SEH Exception 발생.
	std::vector&amp;lt;int&amp;gt; vec{ 1,2,3 };
	std::for_each(std::execution::par_unseq, vec.begin(), vec.end(), [](const int&amp;amp; num) {
		char* a = 0; *a = 5;
	});

	std::cout &amp;lt;&amp;lt; &quot;fin.&quot;; // 출력되지 않음.
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만! SEH 예외가 발생하면 UnhandleExceptionHandler를 통해서 잡을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;noexcept terminate 로직은 내부적으로 C++ 예외만 잡아서 프로그램을 터트리기 때문이다.&lt;/p&gt;</description>
      <category>Programming/C++ &amp;amp; Unreal</category>
      <category>C++</category>
      <category>CPP</category>
      <category>exception</category>
      <category>For_Each</category>
      <category>terminate</category>
      <category>thread</category>
      <author>장형이</author>
      <guid isPermaLink="true">https://developstudy.tistory.com/110</guid>
      <comments>https://developstudy.tistory.com/110#entry110comment</comments>
      <pubDate>Mon, 23 Oct 2023 03:37:14 +0900</pubDate>
    </item>
    <item>
      <title>C# -&amp;gt; C++</title>
      <link>https://developstudy.tistory.com/109</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;C#으로 만 5년을 근무하다가 드디어 C++을 할 기회가 생겨서 이직하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 다시 쌩초보자가 되어 열심히 C++을 공부해야겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두렵고 설렌다.&lt;/p&gt;</description>
      <category>Day Life</category>
      <author>장형이</author>
      <guid isPermaLink="true">https://developstudy.tistory.com/109</guid>
      <comments>https://developstudy.tistory.com/109#entry109comment</comments>
      <pubDate>Sat, 26 Aug 2023 01:16:35 +0900</pubDate>
    </item>
    <item>
      <title>C# perfmon 샘플 코드</title>
      <link>https://developstudy.tistory.com/108</link>
      <description>&lt;pre id=&quot;code_1692979960335&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;internal class Program
{
    const string CategoryName = &quot;JangHyeong&quot;;
    const string CounterName = &quot;Test&quot;;

    static private bool looping = true;

    // 1초마다 0~99 사이의 값으로 카운터를 남긴다.
    static void Loop()
    {
        var random = new Random();
        var counter = new PerformanceCounter(CategoryName, CounterName, false);
        while (looping)
        {
            counter.RawValue = random.Next(100);
            Thread.Sleep(1000);
        }
    }

    static void Main(string[] args)
    {
        // 없으면 카운터를 생성한다.
        if (PerformanceCounterCategory.Exists(CategoryName) == false)
        {
            CounterCreationData testCounterCreationData = new CounterCreationData();
            testCounterCreationData.CounterName = CounterName;
            testCounterCreationData.CounterType = PerformanceCounterType.NumberOfItems32;

            var jangHyeongCounters = new CounterCreationDataCollection();
            jangHyeongCounters.Add(testCounterCreationData);

            PerformanceCounterCategory.Create(CategoryName, &quot;JangHyeong Test Category&quot;, PerformanceCounterCategoryType.SingleInstance, jangHyeongCounters);
        }

        var thread = new Thread(Loop);
        thread.Start();

        Console.WriteLine(&quot;Start.&quot;);
        Console.ReadLine();
        Console.WriteLine(&quot;End.&quot;);

        looping = false;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1초마다 &quot;JangHyeong&quot;-&quot;Test&quot; 카운터에 0~99의 랜덤한 숫자로 지표를 남기는 프로그램 샘플 코드.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트하려고 짰다가 나중에 다시 써먹으려고 포스팅 한다.&lt;/p&gt;</description>
      <category>Programming/C# &amp;amp; Unity</category>
      <category>c#</category>
      <category>perfmon</category>
      <category>Performance Counter</category>
      <author>장형이</author>
      <guid isPermaLink="true">https://developstudy.tistory.com/108</guid>
      <comments>https://developstudy.tistory.com/108#entry108comment</comments>
      <pubDate>Sat, 26 Aug 2023 01:13:28 +0900</pubDate>
    </item>
    <item>
      <title>C# 람다 클로저 원리</title>
      <link>https://developstudy.tistory.com/107</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C#의 람다식의 사용법과 어떻게 작동하는지 간략하게 정리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;람다&lt;/h2&gt;
&lt;pre id=&quot;code_1692974012297&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int number = 5;

// input에 5를 더하여 리턴하는 익명 함수를 담은 변수 action.
var action = (int input) =&amp;gt; { return number + input; };

// 5 + 10이 리턴되어 15가 출력된다.
Console.WriteLine(action(10)); // 출력: 15&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서 &quot;&lt;i&gt;&lt;b&gt;(int&amp;nbsp;input)&amp;nbsp;=&amp;gt;&amp;nbsp;{&amp;nbsp;return&amp;nbsp;number&amp;nbsp;+&amp;nbsp;input;&amp;nbsp;};&lt;/b&gt;&lt;/i&gt;&quot; 구문을 &lt;b&gt;람다 식&lt;/b&gt;이라고 부르며, 함수지만 이름이 없어서 &lt;b&gt;무명 함수&lt;/b&gt;(메서드)라고도 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때, 익명 함수 외부에서 가져온 변수 number를 &lt;b&gt;캡처된 변수&lt;/b&gt;라고 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 캡처가 완료되어 사용가능한 상태가 된 익명 함수를 &lt;b&gt;클로져&lt;/b&gt;라고 부르고, 위 예제에서는 그 클로져를 action이라는 변수가 담고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;캡처는 레퍼런스&lt;/h2&gt;
&lt;pre id=&quot;code_1692974922831&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int number = 5;

// number라는 캡쳐된 변수에 input을 집어 넣는 무명함수.
// action에 캡쳐가 폐쇄된(closure) 무명함수가 저장되었다.
var action = (int input) =&amp;gt; { number = input; };

// action에 인자를 10으로 실행한다.
action(10);

// 실제로 number가 수정되었다.
Console.WriteLine(number); // 출력: 10&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캡쳐된 변수는 레퍼런스로 넘어가서 무명 함수 내에서 수정하면 실제로 캡처된 변수의 값이 변경된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;컴파일러가 생성해내는 람다 코드&lt;/h2&gt;
&lt;pre id=&quot;code_1692976417430&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Program {
    public void Main() {
        int number = 5;
        Console.WriteLine(number);
        
        var action = (int input) =&amp;gt; { return number + input; };
        Console.WriteLine(action(10));
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드를 실제로 컴파일러는 아래의 코드로 생성해 낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1692976461173&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[CompilerGenerated]
private sealed class &amp;lt;&amp;gt;c__DisplayClass0_0
{
    public int number;

    internal int &amp;lt;Main&amp;gt;b__0(int input)
    {
        return number + input;
    }
}

public void Main()
{
    &amp;lt;&amp;gt;c__DisplayClass0_0 &amp;lt;&amp;gt;c__DisplayClass0_ = new &amp;lt;&amp;gt;c__DisplayClass0_0();
    &amp;lt;&amp;gt;c__DisplayClass0_.number = 5;
    Console.WriteLine(&amp;lt;&amp;gt;c__DisplayClass0_.number);
    Func&amp;lt;int, int&amp;gt; func = new Func&amp;lt;int, int&amp;gt;(&amp;lt;&amp;gt;c__DisplayClass0_.&amp;lt;Main&amp;gt;b__0);
    Console.WriteLine(func(10));
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 먼저 클래스를 만드는 코드를 추가한다. (&amp;lt;&amp;gt;c__DisplayClass2_0 클래스)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 익명 함수를 (1.)에서 만든 클래스 안으로 집어넣는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 캡처되어야 할 변수를 클래스 멤버 변수로 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 람다를 사용하는 코드에서 (1.)에서 만든 클래스의 객체를 생성하는 코드로 바꾼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. 객체를 생성한 다음에 캡쳐되어야 할 변수를 클래스의 객체 멤버 변수에 넣는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. 캡처된 변수를 사용하는 부분을 모두 (5.)의 객체 멤버 변수를 참조하도록 수정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위처럼 코드가 생성되기 때문에 클로져가 제대로 작동하며, 레퍼런스 타입과 primitive 타입들 모두 캡쳐 변수라면 레퍼런스로 넘어가는 것처럼 처리가 되게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;람다식은 인라인 함수로 생성되며 인라인 최적화 될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C++도 거의 똑같은 원리로 생성해내니 참고하자.&lt;br /&gt;&lt;a href=&quot;https://sharplab.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://sharplab.io/&lt;/a&gt; 사이트에서 컴파일러 생성 코드를 쉽게 확인 할 수 있다.&lt;/p&gt;</description>
      <category>Programming/C# &amp;amp; Unity</category>
      <category>c#</category>
      <category>closure</category>
      <category>lambda</category>
      <category>람다</category>
      <category>클로저</category>
      <author>장형이</author>
      <guid isPermaLink="true">https://developstudy.tistory.com/107</guid>
      <comments>https://developstudy.tistory.com/107#entry107comment</comments>
      <pubDate>Sat, 26 Aug 2023 00:35:55 +0900</pubDate>
    </item>
  </channel>
</rss>