본문 바로가기
학습 노트/개인학습

250608 일요일 @ 42일차

by 삼색먕 2025. 6. 9.

 

✅C# DummyClient 수동 조작 버전 Flow
* 기본 Flow완성
🔄C# DummyClient 수동 조작으로 Character 생성 까지 만들어보기
* 로그인까지 완료
✅C++ Client수신 > DB > GameClient 캐싱 > Client송신 Flow 제작 및 테스트
➕C# DummyClient Command Reflection / Attribute C#문법 사용하여 함수매핑자동화
➕DummyClient 로직 수정
➕C++ PacketManager -> Packet생성자 매핑 지저분해서 함수제작
➕C++ PacketManager -> Packet핸들러 매핑 지저분해서 함수제작

일단 DummyClient는 어느정도 기본 플로우는 만들어뒀다
그래도 User 캐싱부분은 추가로 제작해야함.
기존 로직을  Flow구성 하면서 수정했더니 오래걸렸다.

문제가 생긴부분은 C++인데 Flow대로 구성해보니까
비효율 적인 부분이  너무많이 보였다
DB에서 Request(프로시저 1개)로 생각했는데
결과에 따라 추가적으로 DB를 작업해서 보내는 경우 현재로는 
Client-> DBRequest(데이터 +client) -> DB에서 데이터를 받아 ClientSend인데 
IOCP작업을 DB가 하는것도 이상하고 추가 DB를 해야할경우 DB가 Send를 
여러번 해야할수도 있는 점이 이상하다고 느꼈다. 구조를 갈아 엎어야 할것 같은느낌이다.

뿐만아니라 packetmanager에서 Handler_패킷 부분에서
DBRequest를 할경우 현재 구조에서는 DBRequest 종류당 
include Request_##.h를 해야한다는점이 당황스러웠다.
정말 이구조가 맞는가 싶었다..

아무래도 구조를 한번 다시 제대로 생각해서 수정이 필요할것같다..


class A : public enable_shared_from_this<A>
	virtual void Func()
	{
		FA(shared_from_this());
	}

class B : public A , public enable_shared_from_this<B>
	void Func() override
	{
		FB(enable_shared_from_this<B>::shared_from_this());
	}

void FA(shared_ptr<A> a)
void FB(shared_ptr<B> b)

int main()
{
	shared_ptr<B> b = make_shared<B>();
	shared_ptr<A> a = b;

	a->Func();
	return 0;
}

C++에서 shared_ptr관련해서도 한가지 알게된사실
이경우에는 raw ptr의 경우에는 자연스럽게 되던겄이지만 
Shared_ptr에서는 _Throw_bad_weak_ptr가 나오기때문에 사용하지 못한다 
디버깅을 해본결과 virtual로 들어간 FB실행전 확인결과 
B객체의 weak_ptr이 상속받은 A의 weak_ptr과 B의 weak_ptr 모두 empty이기때문에 Throw가 걸린다.
왜 안들어가나 확인해보니까 memory.h에서 
template <class _Yty>
struct _Can_enable_shared<_Yty, void_t<typename _Yty::_Esft_type>>
    : is_convertible<remove_cv_t<_Yty>*, typename _Yty::_Esft_type*>::type {
    // is_convertible is necessary to verify unambiguous inheritance
};
B*는 enable_shared_from_this<B>*로 암시적으로 변환이 불가능하다
B*는 enable_shared_from_this<B>* 가 될수도있고 enable_shared_from_this<A>*가 될수도있다보니까
컴파일단에서 저상태의경우 _Wptr이 초기화가 되지 않는다.

즉 Shared_ptr은 상속받더라도 하나의 클래스에서만 해야한다.
이 방식으로 하기위해선

class B : public A
{
public:
	std::shared_ptr<B> GetSelf() {
		return std::static_pointer_cast<B>(shared_from_this());
	}

	void Func() override
	{
		FB(GetSelf());
	}
};

위와 같이 
std::shared_ptr<B> GetSelf() { return std::static_pointer_cast<B>(shared_from_this()); }
처럼 헬퍼 함수를 제작하여 넘겨주면 정상적으로 관리된다.

또한
shared_ptr<B> b = make_shared<B>();
shared_ptr<A> a = b;
생명주기도 확인해보았는데 b,a 둘중 어느게 먼저 사라지더라도
본래의 make_shared<B>()는 정상적으로 사라지지 않고 존재한다
즉 B로생성해서 A로 캐스팅하여 관리하더라도 B가 지워지지 않고,
B로 쓰고있던 어느곳에서 B의 shared_ptr이 사라지더라도 B를 받은 A가 존재한다면 사라지지 않는다

 



 

 

일단 연결은 확인 했다
C#에서 놀라웠던건 Command Reflection / Attribute 문법이었다
Attribute를 이용해  함수에 Attribute지정을 해주고
클래스의 함수를 전부 불러와서 Attribute확인, 매개변수확인해서 Dictionary로 관리하여
명령어를 제작했다.

private static readonly Dictionary<string, CommandEntry> _table;

//미리 정의한 CommandEntry 로 생성해주기
_table = new Dictionary<string, CommandEntry>();

//Command class내부의 모든 static 함수 가져온다 (Client class의 함수는 안가져옴)
var methods = typeof(Command).GetMethods(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);

//
foreach (var method in methods)
{
    //메소드의 Attribute값 있는지 확인, 
    //선언한 CommandAttribute(string name, string description)가 붙어있는지 확인
    var attr = method.GetCustomAttribute<CommandAttribute>();
    if (attr == null)
        continue;

    //파라미터 체크: Client 하나만 받는 함수로 걸러내기
    //메소드의 파라미터를 가져온다
    //파라미터가 1개가 아니면, 1개의 파리미터 타입이 Client가 아니면 캐싱 걸러내기
    var parameters = method.GetParameters();
    if (parameters.Length != 1 || parameters[0].ParameterType != typeof(Client))
        continue;

    //CommandEntry의 변수 2개 만들어주기 (Action / string -> attribute의 discription  )
    //attribute 는 [Attribute명(내부변수1, 내부변수2....)
    //여기서 CommandAttribute 내부에 Name , Description으로 만들어줬기때문에 값을 가져올수 있음.
    //변수의 순서와 [Attribute(1,2)] 전방선언과 맵핑
    var action = (Action<Client>)Delegate.CreateDelegate(typeof(Action<Client>), method);
    _table[attr.Name] = new CommandEntry { Handler = action, Description = attr.Description };
}

[CommandAttribute("exit", "프로그램을 종료합니다.")]
private static void HandleExit(Client client)
{
   Console.WriteLine("프로그램을 종료합니다.");
   Environment.Exit(0);
}

[AttributeUsage(AttributeTargets.Method)]
public sealed class CommandAttribute : Attribute //sealed 이후 상속 불가 C++ final
{
    public string Name { get; }
    public string Description { get; }

    public CommandAttribute(string name, string description)
    {
        Name = name.ToLower();
        Description = description;
    }
}

public class CommandEntry
{
    public Action<Client> Handler;
    public string Description;
}

 

 

'학습 노트 > 개인학습' 카테고리의 다른 글

250611 수요일 @ 45일차  (0) 2025.06.12
250609 월요일 @ 43일차  (0) 2025.06.10
250607 토요일 @ 41일차  (0) 2025.06.08
250606 금요일 @ 40일차 (ODBC MSSQL 연결)  (0) 2025.06.07
250605 목요일 @ 38일차  (0) 2025.06.06

댓글