tuple 형태로 return 되는 값을 이미 선언된 변수로 받고 싶을 때 std::tie 를 이용하면 된다.

std::tuple<bool, int> func()
{
    return { true, 1 };
}

void mainFunc()
{
    auto [ret, value] = func(); // 새로운 로컬 변수로 받을 때

    bool retB {false};
    int valueI {0};
    std::tie(retB, valueI) = func(); // 이미 선언된 변수로 받을 때
}

참고 : std::tie at cppreference.com

728x90

Unity 에디터에서 scene 실행하면 touch 가 되는지 pending changes에 내용은 바뀌지 않았지만 변경된 파일들이 잔뜩 생기는 경우가 있다. 변경하지 않은 파일들을 구분하기 위해 Preference > Other options > Miscellaneous 항목에서 'Check content (hash) when file timestamps is modified to set is as "Changed" 를 체크하면 내용이 바뀌지 않은 파일들은 보이지 않는다.

728x90

윈도우즈에서 여러 working copy 를 업데이트할 때 svn update 명령을 배치 파일에서 바로 사용할 수도 있지만 conflict 등 오류 상황을 처리를 할 때는 TortoiseSVN ui 를 사용하는게 편하다. 윈도우즈 쉘 메뉴에서 update 를 누른 것처럼 배치 파일에서 처리하려면 TortoiseProc.exe 를 이용하면 된다.

> %TortoiseSvnPath%\TortoiseProc.exe /command:update /path:%RepositoryPath% /closeonend:1
# /command: #명령어
# /path: #저장소 경로
# /closeonend: #종료 처리

closeonend 값은 각각 다음과 같은 의미를 나타낸다. /closeonend:2 를 주로 사용하는데 별 문제가 없어도 창이 안닫히는 경우가 있다. 원인은 아직 찾아내지 못했다.

/closeonend:0 자동으로 닫히지 않음
/closeonend:1 에러가 없다면 자동으로 닫힘
/closeonend:2 에러와 충돌이 없다면 자동으로 닫힘
/closeonend:3 에러, 충돌, 병합 상황이 없다면 자동으로 닫힘

 

참고 : 

 

728x90

std::shared_ptr 를 복사 해서 read 하고 update 를 동시에 해도 문제가 없을 줄 알았는데 현실은 access violation 이 발생했다.

#include <iostream>
#include <memory>
#include <thread>

std::shared_ptr<int> g;

void read_g()
{
    long long sum = 0;
    for (int i = 0; i < 1000 * 1000; ++i)
    {
        auto x = g; // read
        if (x)
        {
            sum += *x;
        }
    }
    printf("sum = %lld\n", sum);
}

void write_g()
{
    for (int i = 0; i < 1000 * 1000; ++i)
    {
        auto n = std::make_shared<int>(42);
        g = n; // update
    }
}

int main()
{
    g = std::make_shared<int>(42);
    std::thread t1(read_g);
    std::thread t2(write_g);
    t1.join();
    t2.join();

	return 0;
}

검색해보니 동시에 접근할 경우 문제가 발생할 수도 있는 것 같다.

All member functions (including copy constructor and copy assignment) can be called by multiple threads on different instances of shared_ptr without additional synchronization even if these instances are copies and share ownership of the same object. If multiple threads of execution access the same shared_ptr without synchronization and any of those accesses uses a non-const member function of shared_ptr then a data race will occur; the shared_ptr overloads of atomic functions can be used to prevent the data race.

출처 : https://en.cppreference.com/w/cpp/memory/shared_ptr

shared_ptr 에 대한 atomic 함수들을 사용하면 되는 것 같다.

template< class T >
std::shared_ptr<T> atomic_load( const std::shared_ptr<T>* p );

template< class T >
void atomic_store( std::shared_ptr<T>* p, std::shared_ptr<T> r );

출처 : https://en.cppreference.com/w/cpp/memory/shared_ptr/atomic

c++ 20 에서는 위 함수들은 사라지고 std::atomic<std::shared_ptr<T>> 를 이용해야 하는 것 같다.

template <class T> struct std::atomic<std::shared_ptr<T>>;

참고 : https://en.cppreference.com/w/cpp/memory/shared_ptr/atomic2
728x90

파일 이름 변경할 때 git mv 명령을 사용한다.

> git mv <source> <destination>

참고 : git-mv

728x90

여러 바이너리에서 같은 폴더의 데이터를 참조하는 경우가 많다. 바이너리에서 참조하는 폴더 경로가 실행 경로의 상대경로로 지정되어 있고 바이너리를 수정할 수 없을 때 난감하다. 이럴 때 윈도우즈의 심볼 링크 기능을 사용하면 좋다.

MKLINK [[/D] | [/H] | [/J]] 링크 대상
        /D      디렉터리 바로 가기 링크를 만듭니다. 기본값은 파일 바로 가기
                링크입니다.
        /H      바로 가기 링크 대신 하드 링크를 만듭니다.
        /J      디렉터리 교차점을 만듭니다.
        링크    새 바로 가기 링크 이름을 지정합니다.
        대상    새 링크로 참조되는 절대 경로 또는 상대 경로를
                지정합니다.

명령창(cmd) 에서 mklink 명령어를 이용하면 된다. 

# E:\data 폴더를 .\data 폴더로 링크

> mklink /d data E:\data 


728x90

코드가 많아져서 그런지 빌드 때 아래와 같은 오류가 발생했다.


fatal error C1083: 컴파일러 중간 파일 파일을 열 수 없습니다. '__PATH__\Release\x64\stdafx.obj': Not enough space



32비트 컴파일러 사용시 3GB 영역을 넘게 사용하려고 해서 문제가 발생하는 거라고 한다.

VS 2019 에서는 Configuration Properties > Advanced > Perferred Build Tool Architecture 라는 항목이 생겨서 64-bit 를 선택하면 된다.


VS 2017 에서는 해당 항목이 보이지 않는다. 하지만 vcxproj 에 아래 내용을 추가하면 64 bit 툴을 사용하는 것 같다. vcxproj 에서 'Microsoft.Cpp.defaul.props' Import 항목 아래에 아래 내용을 추가하자.


<PropertyGroup>

    <PreferredToolArchitecture>x64</PreferredToolArchitecture>

</PropertyGroup>



참고 : 


https://stackoverflow.com/questions/60464532/vs2019-fatal-error-c1083-cannot-open-compiler-intermediate-file-xxxx-ipdb-not


Using MSBuild with the 64-bit Compiler and Tools

728x90

함수 포인터보다 std::function 을 사용하는게 더 깔끔해 보이는 것 같다.


template< class R, class... Args >

class function<R (Args...)>;


선언은 반환 형식 R 을 적고 '(', ')' 괄호 안에 입력 형식을 적어준다. 함수, 람다 표현식, 멤버 함수를 대입해서 사용할 수 있다. delegate 를 구현할 때 많이 사용한다.


일반 함수 대입은 아래와 같은 선언하고 사용한다.


// store a free function

std::function<void(int)> f_display = print_num;

f_display(-9);


멤버 함수 대입은 아래와 같이 많이 한다.


struct Foo {

    Foo(int num) : num_(num) {}

    void print_add(int i) const { std::cout << num_+i << '\n'; }

    int num_;

};


// store a call to a member function and object

using std::placeholders::_1;

const Foo foo(314159);

std::function<void(int)> f_add_display2 = std::bind( &Foo::print_add, foo, _1 );

f_add_display2(2);


다른 사용법도 있으니 아래 예제를 참고하자.


#include <functional>

#include <iostream>

 

struct Foo {

    Foo(int num) : num_(num) {}

    void print_add(int i) const { std::cout << num_+i << '\n'; }

    int num_;

};

 

void print_num(int i)

{

    std::cout << i << '\n';

}

 

struct PrintNum {

    void operator()(int i) const

    {

        std::cout << i << '\n';

    }

};

 

int main()

{

    // store a free function

    std::function<void(int)> f_display = print_num;

    f_display(-9);


    // store a lambda

    std::function<void()> f_display_42 = []() { print_num(42); };

    f_display_42();

 

    // store the result of a call to std::bind

    std::function<void()> f_display_31337 = std::bind(print_num, 31337);

    f_display_31337();

 

    // store a call to a member function

    std::function<void(const Foo&, int)> f_add_display = &Foo::print_add;

    const Foo foo(314159);

    f_add_display(foo, 1);

    f_add_display(314159, 1);

 

    // store a call to a data member accessor

    std::function<int(Foo const&)> f_num = &Foo::num_;

    std::cout << "num_: " << f_num(foo) << '\n';

 

    // store a call to a member function and object

    using std::placeholders::_1;

    std::function<void(int)> f_add_display2 = std::bind( &Foo::print_add, foo, _1 );

    f_add_display2(2);

 

    // store a call to a member function and object ptr

    std::function<void(int)> f_add_display3 = std::bind( &Foo::print_add, &foo, _1 );

    f_add_display3(3);

 

    // store a call to a function object

    std::function<void(int)> f_display_obj = PrintNum();

    f_display_obj(18);

}


위 예제에도 나왔지만 std::function 에 대입할 때 std::bind를 이용하게 된다. 

std::function 변수와 함수 선언 순서가 다른 경우에 std::bind 에 std::placeholders 이용해서 대입하는 경우가 많다.


아래 예제를 보면 a, b, c 를 받아서 std::bind 와 placeholders::_1, placeholders::_2 를 이용해 f( b, 42, a, n, 7) 로 호출하는 법을 알 수 있다. 


void f(int n1, int n2, int n3, const int& n4, int n5)

{

    std::cout << n1 << ' ' << n2 << ' ' << n3 << ' ' << n4 << ' ' << n5 << '\n';

}


// demonstrates argument reordering and pass-by-reference

int n = 7;

// (_1 and _2 are from std::placeholders, and represent future

// arguments that will be passed to f1)

auto f1 = std::bind(f, _2, 42, _1, std::cref(n), n);

n = 10;

f1(1, 2, 1001); // 1 is bound by _1, 2 is bound by _2, 1001 is unused

                   // makes a call to f(2, 42, 1, n, 7)


멤버 변수 객체를 묶어 숨겨서 호출하는 쪽에서 모르게 할 수 있어 delegate 만들 때 유용한 것 같다.


Foo foo;

auto f3 = std::bind(&Foo::print_sum, &foo, 95, _1);

f3(5);


다른 예제는 아래 예제를 참고하자.


#include <random>

#include <iostream>

#include <memory>

#include <functional>

 

void f(int n1, int n2, int n3, const int& n4, int n5)

{

    std::cout << n1 << ' ' << n2 << ' ' << n3 << ' ' << n4 << ' ' << n5 << '\n';

}

 

int g(int n1)

{

    return n1;

}

 

struct Foo {

    void print_sum(int n1, int n2)

    {

        std::cout << n1+n2 << '\n';

    }

    int data = 10;

};

 

int main()

{

    using namespace std::placeholders;  // for _1, _2, _3...

 

    // demonstrates argument reordering and pass-by-reference

    int n = 7;

    // (_1 and _2 are from std::placeholders, and represent future

    // arguments that will be passed to f1)

    auto f1 = std::bind(f, _2, 42, _1, std::cref(n), n);

    n = 10;

    f1(1, 2, 1001); // 1 is bound by _1, 2 is bound by _2, 1001 is unused

                    // makes a call to f(2, 42, 1, n, 7)

 

    // nested bind subexpressions share the placeholders

    auto f2 = std::bind(f, _3, std::bind(g, _3), _3, 4, 5);

    f2(10, 11, 12); // makes a call to f(12, g(12), 12, 4, 5);

 

    // common use case: binding a RNG with a distribution

    std::default_random_engine e;

    std::uniform_int_distribution<> d(0, 10);

    auto rnd = std::bind(d, e); // a copy of e is stored in rnd

    for(int n=0; n<10; ++n)

        std::cout << rnd() << ' ';

    std::cout << '\n';

 

    // bind to a pointer to member function

    Foo foo;

    auto f3 = std::bind(&Foo::print_sum, &foo, 95, _1);

    f3(5);

 

    // bind to a pointer to data member

    auto f4 = std::bind(&Foo::data, _1);

    std::cout << f4(foo) << '\n';

 

    // smart pointers can be used to call members of the referenced objects, too

    std::cout << f4(std::make_shared<Foo>(foo)) << '\n'

              << f4(std::make_unique<Foo>(foo)) << '\n';

}


참고

* std::bind

* std:function

728x90

터미널 서비스 동시 접속을 기본 1개만 허용한다. 여러개 사용하고 싶을 때 RDP Wrapper 라는 프로그램을 사용하면 된다.

다운로드

https://github.com/stascorp/rdpwrap/releases 에서 RDPWrap-v1.6.2.zip 을 다운받자.

P.S. 크롬에서는 악성 프로그램으로 취급된다. 위험성에 대해서는 각자 판단하자.

설치

다운받은 파일의 압축을 푼다.
압축을 푼 폴더에서 install.bat 과 update.bat 을 실행한다. (관리자 권한이 필요하다. )

설정

RDPConf.exe 파일을 이용해서 설치를 확인하자.

Listener state 열에 '[not supported]'가 보이면 동작하지 않는다.

rdpwrap.ini

Windows 10 버전 2004 (OS 빌드 19041.630) 을 위한 설정 파일이라고 한다.
C:\Program Fiels\RDP Wrapper 디렉토리의 rdpwrap.ini 에 덮어쓰자.
RDPConf.exe 파일로 다시 확인하면 'fully supported' 로 바뀐 것을 확인할 수 있다.

윈도우 + R 을 눌러 실행창을 띄우고 services.msc 를 입력해 서비스 창을 띄운다.
변경 적용을 위해 Remote Desktop Services 를 재시작한다.

RDPWrap에 포함된 RDPCheck.exe 프로그램이나 다른 PC에서 터미널 서비스 접근해서 멀티세션이 허용되는지 확인하면 된다.

출처 : https://security-nanglam.tistory.com/476

728x90

특정 변수를 thread 별로 사용하고 싶을 때 붙인다. 


현재 thread 를 전역 변수에 할당하고 참조할 때 편하다. thread 별 memory allocator 랄지 lock queue 를 만들어서 데드락 디버깅할 때도 유용하다.


thread_local Thread* GCurrentThread; 


참고 : MSDN - thread_local (C++11)

728x90

+ Recent posts