C#의 비동기 구조가 너무 낯설어서 힘들당…
내가 얼마나 좁은 지식만 가지고 있었는지 절감중..
C# Visitor
바로 이전글인 double dispatching 이랑 비교해서 생각해보자
using System;
using System.Collections.Generic;
namespace ConsoleApp3
{
// 1. Cat, Dog 간 Visitor 구현 코드
public interface IVisitableElement
{
void Accept(IFindSameVisitor visitor);
}
public class Animal
{
}
public class Cat : Animal, IVisitableElement
{
public void Accept(IFindSameVisitor visitor) => visitor.Visit(this);
}
public class Dog : Animal, IVisitableElement
{
public void Accept(IFindSameVisitor visitor) => visitor.Visit(this);
}
public partial interface IFindSameVisitor
{
void Visit(Cat visitor);
void Visit(Dog visitor);
}
public partial class FindSameCat : IFindSameVisitor
{
public void Visit(Cat visitor)
{
Console.WriteLine("Cat-Cat = Same");
}
public void Visit(Dog visitor)
{
Console.WriteLine("Cat-Dog = Cute");
}
}
public partial class FindSameDog : IFindSameVisitor
{
public void Visit(Cat visitor)
{
Console.WriteLine("Dog-Cat = Cute");
}
public void Visit(Dog visitor)
{
Console.WriteLine("Dog-Dog = Same");
}
}
internal class Program
{
static void Main(string[] args)
{
// 1차 구현 실행
var animals1 = new List<IVisitableElement>
{
new Cat(), new Dog()
};
var catVisitor = new FindSameCat();
var dogVisitor = new FindSameDog();
animals1.ForEach(a => a.Accept(catVisitor));
animals1.ForEach(a => a.Accept(dogVisitor));
Console.WriteLine();
// 2차 Mouse 추가 구현 실행
var animals2 = new List<IVisitableElement>
{
new Cat(), new Dog(), new Mouse()
};
var mouseVisitor = new FindSameMouse();
animals2.ForEach(a => a.Accept(catVisitor));
animals2.ForEach(a => a.Accept(dogVisitor));
animals2.ForEach(a => a.Accept(mouseVisitor));
}
}
// 2. Mouse가 추가된 경우 추가 구현 코드
public class Mouse : Animal, IVisitableElement
{
public void Accept(IFindSameVisitor visitor) => visitor.Visit(this);
}
public partial interface IFindSameVisitor
{
void Visit(Mouse visitor);
}
public partial class FindSameCat : IFindSameVisitor
{
public void Visit(Mouse visitor)
{
Console.WriteLine("Cat-Mouse = Mustache");
}
}
public partial class FindSameDog : IFindSameVisitor
{
public void Visit(Mouse visitor)
{
Console.WriteLine("Dog-Mouse = Four Legs");
}
}
public partial class FindSameMouse : IFindSameVisitor
{
public void Visit(Mouse visitor)
{
Console.WriteLine("Mouse-Mouse = Same");
}
public void Visit(Cat visitor)
{
Console.WriteLine("Mouse-Cat = Mustache");
}
public void Visit(Dog visitor)
{
Console.WriteLine("Mouse-Dog = Four Legs");
}
}
}
C# Double Dispatch
using System;
namespace ConsoleApp3
{
internal class Program
{
static void Main(string[] args)
{
Animal a = new Cat();
Animal a1 = new Cat();
Animal b = new Dog();
Animal b1 = new Dog();
Animal c = new Mouse();
Animal c2 = new Mouse();
a.FindSamething(a1);
a.FindSamething(b);
a.FindSamething(c);
Console.WriteLine();
b.FindSamething(a);
b.FindSamething(b1);
b.FindSamething(c);
Console.WriteLine();
c.FindSamething(a);
c.FindSamething(b);
c.FindSamething(c2);
Console.WriteLine();
}
}
// 1. Cat, Dog 간 Double Dispatching 이 필요한 경우 구현 코드
partial class Animal
{
public virtual void FindSamething(Animal animal) => Console.WriteLine("Animal FindSamething");
public virtual void FindSamethingImpl(Cat obj) => Console.WriteLine("Animal-Cat FindSamethingImpl");
public virtual void FindSamethingImpl(Dog obj) => Console.WriteLine("Animal-Dog FindSamethingImpl");
}
partial class Cat : Animal
{
public override void FindSamething(Animal animal)
{
animal.FindSamethingImpl(this);
}
public override void FindSamethingImpl(Cat obj) => Console.WriteLine("Cat-Cat = Same");
public override void FindSamethingImpl(Dog obj) => Console.WriteLine("Cat-Dog = Cute");
}
partial class Dog : Animal
{
public override void FindSamething(Animal animal)
{
animal.FindSamethingImpl(this);
}
public override void FindSamethingImpl(Dog obj) => Console.WriteLine("Dog-Dog = Same");
// 이렇게 리다이렉션 하는 경우 호출객체와 인자가 뒤바뀌기 때문에
// this.FindSamethingImpl 와 obj.FindSamethingImpl 의 결과가 다른 경우에는 별도 구현해야함.
public override void FindSamethingImpl(Cat obj) => obj.FindSamethingImpl(this);
}
// 2. Mouse가 추가된 경우 추가 구현 코드
partial class Animal
{
public virtual void FindSamethingImpl(Mouse m) => Console.WriteLine("Animal-Mouse FindSamethingImpl");
}
partial class Cat
{
public override void FindSamethingImpl(Mouse m) => m.FindSamethingImpl(this);
}
partial class Dog
{
public override void FindSamethingImpl(Mouse m) => m.FindSamethingImpl(this);
}
partial class Mouse : Animal
{
public override void FindSamething(Animal animal)
{
animal.FindSamethingImpl(this);
}
public override void FindSamethingImpl(Cat c) => Console.WriteLine("Mouse-Cat = Mustache");
public override void FindSamethingImpl(Dog d) => Console.WriteLine("Mouse-Dog = Four Legs");
public override void FindSamethingImpl(Mouse m) => Console.WriteLine("Mouse-Mouse = Same");
}
}
C++ variant 기초 사용 & 상속 캐스팅과 비교
#include <iostream>
#include <variant>
#include <vector>
#include <chrono>
using namespace std;
struct Object
{
};
struct Object0 : public Object
{
int value;
};
struct Object1 : public Object
{
vector<int> values;
};
struct Object2 : public Object
{
string values;
};
struct Object3 : public Object
{
wstring values;
};
int main()
{
variant<Object0, Object1, Object2, Object3> var;
cout << "size" << endl;
cout << "Object0 : " << sizeof(Object0) << endl;
cout << "Object1 : " << sizeof(Object1) << endl;
cout << "Object2 : " << sizeof(Object2) << endl;
cout << "Object2 : " << sizeof(Object3) << endl;
cout << "variant : " << sizeof(var) << endl;
var = Object0(); // 이 코드가 없으면 아래 값을 설정할때 에러가 발생함. variant의 타입이 정해지지 않기 때문인듯.
get<Object0>(var).value = 0;
cout << "variant Object0 value: " << get<Object0>(var).value << endl;
constexpr int COUNT = 100'000'000;
{
auto caseStr = "case variant"s;
auto startTime = chrono::steady_clock::now();
volatile int64_t accValue = 0;
for (int i = 0; i < COUNT; i++)
{
Object0& rObj = get<Object0>(var);
rObj.value = i;
accValue += rObj.value;
}
auto endTime = std::chrono::steady_clock::now();
cout << format("{}({}) : {}ms, accValue={}",
caseStr,
COUNT,
chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime).count(),
(int) accValue) << endl;
}
{
Object* pObj = new Object0();
auto caseStr = "case pointer"s;
auto startTime = chrono::steady_clock::now();
volatile int64_t accValue = 0;
for (int i = 0; i < COUNT; i++)
{
Object0& rObj = *(static_cast<Object0*>(pObj));
rObj.value = i;
accValue += rObj.value;
}
auto endTime = std::chrono::steady_clock::now();
cout << format("{}({}) : {}ms, accValue={}",
caseStr,
COUNT,
chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime).count(),
(int)accValue) << endl;
}
return 0;
}
size
Object0 : 4
Object1 : 24
Object2 : 32
Object2 : 32
variant : 40
variant Object0 value: 0
case variant(100000000) : 47ms, accValue=887459712
case pointer(100000000) : 44ms, accValue=887459712
variant가 사알짝 느림.
하지만 한번 설정한 타입에 대해서 이후 다른 타입으로 접근시 익셉션을 발생시켜 준다는 점에서 안정성은 좋을듯.
타입세이프 유니온이라는 이름에 걸맞는듯.
아이 키우기 감상
이제 아이가 태어난지 5년이 되었다.
새삼 이런 저런 생각이 들어 한번 글로 써 보았다.
나는 인생이 여러 개의 원을 그리고 있다고 생각한다. 내가 산 삶 중 생활 방식에서 큰 변화가 있었던 시기를 원으로 표현하면, 잘 기억나지 않지만 잘 뛰어놀았던 것 같은 유년기의 원, 친구들과 놀고 게임을 많이 했던 초등학교, 중학교의 원, 나름 수험 생활을 했던 고등학교의 원, 스스로 공부하고 또 게임을 즐겨했던 대학교의 원, 그리고 직장 생활의 원.
결혼을 하지 않고, 아이를 낳지 않았으면 직장 생활의 원에서 더 이상 새로운 것을 찾지 못하는 원을 빙글빙글 돌고 있지는 않았을까? 어쩌면 개인적 성공이나 어떤 일로 다른 원을 그리게 되었을 지도 모르겠다. 하지만 확실한 것은 아이와 그리는 부모로서의 원은 내가 자라나면서 그렸던 원과 전혀 다르다. 마치 아이가 그리고 있는 원의 바깥을 둘러싸고 따라가는 원을 그리고 있는 것 같다.
어쩌면 이런 원을 그리는 것이 아이가 위주로 된 삶처럼 보일 수도 있겠지만, 아이가 그리는 원을 보면서 나의 새로운 원을 그리는 것은 언제나 일상 속에 새로움이 있다.
여행을 떠나서 다시 집으로 돌아와 소파에 누우면서 역시 집이 최고라고 말하는 내 성향 때문일지도 모르겠지만 , 새로운 것으로 느끼는 재미와 즐거움이 지겨운 일상이 있지 않으면 느낄 수 없는 것이라는 것을 이제는 알고 있다.
아이는 아마 스스로의 원을 그리겠지. 아이는 부모가 언제나 옆에 있는 것이 당연하여 그 원 주변에서 함께 원을 그리는 것을 잘 모를 것이다. 내가 그랬던 것 처럼. 나는 아이가 원을 그리는 것을 멀리서 지켜볼 때 도 있고, 가끔은 원 모양이 너무 찌그러지지 않게 잔소리도 할 것이고, 다른 사람이 아이가 그리는 원을 망치지 않도록 나서서 막아주는 원을 그릴 것이다. 나의 부모님이 그러하셨던 것 처럼.
C++ vector reallocate action with noexcept
#include <iostream>
#include <vector>
class MyClass
{
private:
std::string _name{};
public:
MyClass(const std::string& name)
:_name(name)
{
std::cout << "create : " << _name << std::endl;
}
MyClass(const MyClass& other) noexcept
{
std::cout << "copy : " << other._name << std::endl;
}
// "noexcept"가 없으면 move가 아닌 copy가 불린다.
MyClass(MyClass&& other) noexcept
{
std::cout << "move : " << other._name << std::endl;
}
};
int main() {
std::vector<MyClass> list;
list.reserve(2);
list.emplace_back("a1");
list.emplace_back("a2");
list.emplace_back("a3");
}
C++ 시간함수 성능비교 #1
#include <iostream>
#include <thread>
#include <chrono>
#include <mutex>
#include <atomic>
#include <windows.h>
auto main() -> int
{
std::cout << "start" << std::endl;
constexpr int COUNT = 10000000;
{
auto startTime = std::chrono::steady_clock::now();
long long accCount = 0;
for (int i = 0; i < COUNT; i++)
{
volatile auto now = std::chrono::steady_clock::now();
}
auto endTime = std::chrono::steady_clock::now();
std::cout << "std std::chrono steady clock : "
<< std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime).count()
<< "ms"
<< ", accCount : " << accCount << std::endl;
}
{
auto startTime = std::chrono::steady_clock::now();
auto initTime = std::chrono::system_clock::now();
long long accCount = 0;
for (int i = 0; i < COUNT; i++)
{
volatile auto now = std::chrono::system_clock::now();
}
auto endTime = std::chrono::steady_clock::now();
std::cout << "std std::chrono system clock : "
<< std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime).count()
<< "ms" << std::endl;
}
{
auto startTime = std::chrono::steady_clock::now();
SYSTEMTIME stInit;
GetSystemTime(&stInit);
long long accCount = 0;
for (int i = 0; i < COUNT; i++)
{
SYSTEMTIME stNow;
GetSystemTime(&stNow);
volatile WORD sec = stNow.wSecond;
}
auto endTime = std::chrono::steady_clock::now();
std::cout << "windows : "
<< std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime).count()
<< "ms"
<< ", accCount : " << accCount << std::endl;
}
{
auto startTime = std::chrono::steady_clock::now();
bool isShutdown = false;
std::atomic<SYSTEMTIME> stNow;
auto timer = std::thread([&isShutdown, &stNow]() {
while (isShutdown == false)
{
{
SYSTEMTIME now;
GetSystemTime(&now);
stNow = now;
}
::Sleep(0);
}
});
for (int i = 0; i < COUNT/10; i++)
{
SYSTEMTIME now = stNow;
for (int i = 0; i < 10; i++)
{
volatile WORD sec = now.wSecond;
}
}
auto endTime = std::chrono::steady_clock::now();
std::cout << "Threading windows : "
<< std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime).count()
<< "ms" << std::endl;
isShutdown = true;
timer.join();
}
}
음… 루프 숫자를 올렸을때 시간이 선형적으로 증가하는걸로 봐서.. 대충 최적화 되서 함수콜이 날아가 버리지 않고 정직하게 호출됬다고 생각됨.
Threading windows는 시간 관련 함수의 성능 때문에 스레드로 빼서 시간을 업데이트하는 스레드가 별도로 있고 다른 스레드는 그 값을 가져와서 쓰는 부분을 모사한것. (일반 콜에 비해서 시간 업데이트는 가끔. 여러 스레드가 업데이트된 값을 가져다가 쓰는 형태)
chrono의 성능은 나쁘지 않음.
시간이 살짝 부정확 하더라도 함수 콜 숫자를 줄이고 싶다면 스레딩
VisualStudio 빌드 이벤트
뭐 복사하거나 그런거.. 기본적으론 한줄만 넣을수 있고, 여러 동작을 처리하려면 스크립트 파일을 실행하도록 해야 할것 같은데… 꼼수로 두줄이상 넣는방법
기본 한줄
xcopy /y /d /e “$(SolutionDir)configs\Common*” “$(OutDir)”
두줄
xcopy /y /d /e “$(SolutionDir)configs\Common*” “$(OutDir)” & xcopy /y /d /e “$(SolutionDir)configs\$(ProjectName)*” “$(OutDir)”
명령 사이에 & 를 넣는다.
C++ filesystem replace_extention
BOOL Program::LoadJsonConfig()
{
namespace fs = std::filesystem;
wchar_t exePathStr[4096];
GetModuleFileName(NULL, exePathStr, 4096);
fs::path exePath = exePathStr;
fs::path jsonConfPath = exePath.replace_extension("config.json");
if (fs::is_regular_file(jsonConfPath) == false)
{
return FALSE;
}
std::ifstream jsonConfFile(jsonConfPath);
try
{
jsonConfFile >> m_jsonConfig;
}
catch (...)
{
jsonConfFile.close();
return FALSE;
}
jsonConfFile.close();
return TRUE;
}
C++11에 추가된 filesystem으로 실행된 exe과 동일한 이름의 config.json 파일을 읽는 코드.
c++ filesystem 은 이제 다른 스크립트언어에 지원하는 file 처리 내용과 유사하게 지원해 줘서 c++ 에서 이런 저런 내용 처리하기가 많이 편해진것 같다.
예전에 텍스트 파일 일괄 처리할 일이 있었는데 c++에서 정규표현식도 지원되고 파일시스템도 지원되니 가끔 써서 어색한 스크립트보다 그냥 자주써서 편한 C++로 작성하고 돌림.
작성 후 컴파일 해서 적용해야 한다는게 단점이지만 실행속도가 빠르니 대용량 처리할때는 나름 장점이 있다.
VS2017 과 VS2019 프로젝트
둘의 관계는 참으로 이상하다.
둘다 설치한 상태에서 VS2017로 만든 프로젝트는 VS2019로 열리고
VS2019로 만든 프로젝트는 VS2017로 열린다.
VS2017이었을 VS2019로 열리던 프로젝트는 VS2019로 마이그레이션한 이후 VS2017로 열린다!