日常开发遇到的一些坑,记录一下挺有意思的
使用MSYS2
windows api和Linux api不兼容的问题,例如read等等函数只能在linux上使用,msys2可以轻松编译为linux设计的大部分软件,例如qemu、tor等。主要参考:
https://polyos.iscas.ac.cn/docs/developer-guides/build-qemu/on-windows/
这里以编译tor为例子:
git clone https://gitlab.torproject.org/tpo/core/tor cd tor
现在启用 msys2 的终端环境
13013@WIN-7JV21MSHSU8 UCRT64 /d/Github/Joe1sn/tor $ ls -CF acinclude.m4 Cargo.lock ChangeLog CODE_OF_CONDUCT contrib/ doc/ INSTALL m4/ README.md scripts/ warning_flags.in autogen.sh* Cargo.toml changes/ configure.ac CONTRIBUTING Doxyfile.in LICENSE Makefile.am ReleaseNotes src/
可能得先装几个包
pacman -S --needed autoconf automake libtool make autoconf-wrapper mingw-w64-ucrt-x86_64-toolchain
首先运行autogen.sh,可能有版本过低等情况,可以问AI怎么解决
$ ./autogen.sh /usr/bin/autoreconf ): you should use literals AC_CHECK_FUNCS(\ ../autoconf-2.73/lib/autoconf/functions.m4:118: AC_CHECK_FUNCS is expanded from... configure.ac:700: the top level ): you should use literals AC_CHECK_FUNCS(vsnprintf ../autoconf-2.73/lib/autoconf/functions.m4:118: AC_CHECK_FUNCS is expanded from... configure.ac:700: the top level ): you should use literals AC_CHECK_FUNCS(\ ../autoconf-2.73/lib/autoconf/functions.m4:118: AC_CHECK_FUNCS is expanded from... configure.ac:785: the top level ): you should use literals AC_CHECK_FUNCS(\ ../autoconf-2.73/lib/autoconf/functions.m4:118: AC_CHECK_FUNCS is expanded from... configure.ac:810: the top level ): you should use literals AC_CHECK_FUNCS(\ ../autoconf-2.73/lib/autoconf/functions.m4:118: AC_CHECK_FUNCS is expanded from... configure.ac:942: the top level ): you should use literals AC_CHECK_FUNCS( ../autoconf-2.73/lib/autoconf/functions.m4:118: AC_CHECK_FUNCS is expanded from... configure.ac:942: the top level ): you should use literals: AC_CHECK_FUNCS(\ //////////////// /// //////////////// $ pacman -S autoconf2.69 $ WANT_AUTOCONF=2.69 ./autogen.sh
然后就和linux一样了
./configure --enable-static-tor \ --with-libevent-dir=$MINGW_PREFIX/lib \ --with-openssl-dir=$MINGW_PREFIX/lib \ --with-zlib-dir=$MINGW_PREFIX/lib \ --disable-unittests \ --disable-module-dirauth \ --disable-manpage \ --disable-html-manual \ --disable-asciidoc make -j 16
最后的编译产物在src/app下
IDA 引用搜索图
View -> Open Subviews -> proximity broswer
得出调用关系
c++的依赖注入
比如在游戏、工具当中,我们需要频繁的访问一些服务,例如渲染器、网络连接,往往使用的是全局单例
Renderer& GetRenderer () ; Input& GetInput () ;
但是工程实践往往表明再游戏中的单例模式往往非常糟糕
隐式依赖 :看一个场景的构造函数,根本不知道它用了哪些服务,得翻实现。
无法测试 :测试时没法替换成 mock,因为全局对象写死了。
初始化顺序 / 生命周期混乱 :全局对象的构造销毁顺序难控。
我们可以通过一个上下文管理器,将这些理应全局单例的东西,按照引用传递过去
#pragma once namespace engine { class Renderer ; class Camera ; struct Context { Renderer& renderer; Camera& camera; }; }
Context 里只有引用成员,不需要知道这些类的完整定义(引用大小固定,不需要完整类型)。所以用前置声明就够了,好处:
编译解耦:任何 #include "Context.h" 的文件不会被迫拉进 Renderer/Input/Audio 的一大堆头文件,编译更快。
减少循环依赖:服务和 Context 之间不容易互相 include。
这是 C++ 头文件设计的经典手法——能用前置声明就别 include。
并且其中的按引用使用可以删除隐式的拷贝复制函数,毕竟我们只需要使用其中的功能即可,不需要再重新复制一个对象来使用
SDL3进行游戏制作的缩放问题
画面提交给camera,然后camera使用sdl_render进行缩放操作,可以适配窗口大小不一致的问题,于是游戏所有逻辑与绘制坐标都基于 1280×720 的"逻辑画布"。SDL 的逻辑分辨率功能会把这张画布等比放大(不拉伸)映射到实际窗口,于是绘制代码一行都不用改,背景/HUD 自动跟着缩放。
在 Camera 构造函数里设置逻辑画布与呈现模式:
Camera::Camera (SDL_Renderer* sdlRenderer) : renderer (sdlRenderer) { SDL_SetRenderLogicalPresentation (renderer, 1280 , 720 , SDL_LOGICAL_PRESENTATION_LETTERBOX); timer_shake.set_one_shot (true ); timer_shake.set_on_timeout ([&]() { is_shaking = false ; reset (); }); }
测试的时候,创建窗口为可变大小,可以在游戏添加设置选项从而固定分辨率的修改
window = SDL_CreateWindow ("gamer" , 1920 , 1080 , SDL_WINDOW_RESIZABLE); SDL_MaximizeWindow (window);
c++ vector 扩容的野指针问题
c的STL容器存在很多抽象的分配逻辑,这些没有直接表现出来的东西配合指针让c 问题频发
#include <iostream> #include <cstdio> #include <vector> int main () { std::vector<int > container = { 1 ,2 ,3 ,4 ,5 ,6 }; int * ptr = &container[0 ]; std::cout << "vector capacoty: " << container.capacity () << " with size: " << container.size () << "\n" ; printf ("ptr at 0x%p : %d\n" , ptr, *ptr); return 0 ; }
vector capacoty: 6 with size: 6 ptr at 0x00987618 : 1
当触发vector的扩容机制后
#include <iostream> #include <cstdio> #include <vector> int main () { std::vector<int > container = { 1 ,2 ,3 ,4 ,5 ,6 }; int * ptr = &container[0 ]; std::cout << "vector capacoty: " << container.capacity () << " with size: " << container.size () << "\n" ; printf ("ptr at 0x%p : %d\n" , ptr, *ptr); for (size_t i = 0 ; i < 0x100 ; i++) { container.push_back (0xFF ); } std::cout << "------\n" ; std::cout << "vector capacoty: " << container.capacity () << " with size: " << container.size () << "\n" ; printf ("ptr at 0x%p : %d\n" , ptr, *ptr); return 0 ; }
vector capacoty: 6 with size: 6 ptr at 0x017C6488 : 1 ------ vector capacoty: 316 with size: 262 ptr at 0x017C6488 : -572662307
这个时候获得了一个野指针 ptr,利用野指针就可以创建任意读写
多态的概念
和继承不同的是,他是根据“动作”的不同进行的分类。
在c中想要有一个数组存放既有int类型又有string类型,类似python中的{123, "AAA", "BBB", 456},但是c 数组中只能存在一个类型,所以不可能。
但是我们可以定义一个type类型,int和string是他的子类,由多态的概念,可以创建一个 type* a[0x10]这样的数组,将int和string类型放在里面,其中需要的是父类引用 。虽然在c++中我们可以直接传入对象的地址到数组中即可 。
多态允许我们使用统一的接口来操作不同的对象,而具体执行哪个操作,则由对象自身的实际类型来决定。
多态存在的三个必要条件
继承 :必须存在继承关系。
重写 :子类必须对父类的方法进行重写(Override)。
父类引用指向子类对象 :这是实现多态的关键代码形式。
class Animal { String name; public Animal (String name) { this .name = name; } public void eat () { System.out.println(name + "正在吃东西。" ); } public void makeSound () { System.out.println("动物发出声音。" ); } } class Dog extends Animal { public Dog (String name) { super (name); } @Override public void makeSound () { System.out.println(name + "汪汪叫!" ); } public void fetch () { System.out.println(name + "在接飞盘。" ); } } class Cat extends Animal { public Cat (String name) { super (name); } @Override public void makeSound () { System.out.println(name + "喵喵叫!" ); } } public class Test { public static void main (String[] args) { Animal myDog = new Dog ("旺财" ); Animal myCat = new Cat ("咪咪" ); myDog.makeSound(); myCat.makeSound(); myDog.eat(); myCat.eat(); Animal[] animals = {new Dog ("小黑" ), new Cat ("小白" )}; animalConcert(animals); } public static void animalConcert (Animal[] animals) { for (Animal animal : animals) { animal.makeSound(); } } }
旺财汪汪叫! 咪咪喵喵叫! 旺财正在吃东西。 咪咪正在吃东西。 小黑汪汪叫! 小白喵喵叫!
power toys键盘映射失效
vscode使用管理员权限运行时使用的是映射之前的输入
普通模式下使用的时映射后的输入
目前还没找大解决方法
python sqlalchemy 查询结果扩大化
def get_self_info (uuid: str ): tasks = session.query(SelfInfo).filter (uuid == uuid).all () return tasks
很明显的typo错误,uuid == uuid是恒成立的,所以会返回所有的结果,导致某个用户获得全部的结果
应当改为
def get_self_info (need_uuid: str ): tasks = session.query(SelfInfo).filter (SelfInfo.uuid == need_uuid).all () return tasks
甚至
def get_self_info (uuid: str ): tasks = session.query(SelfInfo).filter (SelfInfo.uuid == uuid).all () return tasks
c++ Qt6 emit 时undefine
原因是类中没有添加QT的宏定义Q_OBJECT,正常应该如下
class BeaconTable :public BasicTable<TargetInfo>{ Q_OBJECT signals: void doA (int Id) ; void doB (int Id) ; };
抽象类中不能使用该宏定义
c++ 抽象类
template <typename T>class Table :{}
在cpp中要
template <typename T>void Table<T>::deleteRow (int row){}
并且明确具体的抽象类
template class Table <int >;template class Table <char *>;
python-asyncio.StreamReader read不完全
比如期待 data=read(n)但是len(data) < n是因为缓冲区大小或者去处理其他的read导致一次await read无法读取完毕,但是可以利用循环分块读取
while len (data) < file_len: chunk = await reader.read(file_len - len (data)) if not chunk: break data += chunk
python-sqlalchemy偶尔出现 tuple Index out of range
model如下
class Sessions (Base ): '''Session used in cookie''' __tablename__ = 'sessions' id = Column(Integer, primary_key=True , autoincrement=True ) created_at = Column(Integer, default=lambda : int (time.time()), nullable=False ) username = Column(String(32 ), nullable=False ) token = Column(String(64 ), nullable=False )
查询controller如下
def check_session (stoken: str ) -> bool : from db import session exists = session.query( Sessions.id ) .filter (Sessions.token == stoken) .all () return len (exists>=1 )
可能有其他错误,并且只是偶尔发生
原因:DBAPI中的并发问题,一个连接不能同时处理大量信息
解决:在这个controller中新建一个连接
class ConnDB : self.__engine = create_engine( 'sqlite:///{}' .format (self.__path), connect_args={"check_same_thread" : False , "timeout" : 30 , "isolation_level" : "IMMEDIATE" }, pool_pre_ping=True , echo=False ) self.__session = sessionmaker(bind=self.__engine, autocommit=False , autoflush=False ,) Session = conn_db.get_session() def check_session (stoken: str ) -> bool : db = Session() exists = db.query( db.query(Sessions.id ) .filter (Sessions.token == stoken) .exists() ).scalar() db.close() return exists
vscode鼠标拖动代码页分栏失效/没反应
环境:windows
解决方法:cmd+shift+p 运行两次 Developer: Toggle Screencast Mode
if-else与++i --i
写if最好先把else也写上,i++是语句结束后i+1,++i是立即加
左值与右值
左值:表达式左边的值,可以被查找到地址的、持久存在的值
右值:字面量,或者std::move后的左值
左值引用:就是变量的别名
右值引用:绑定到临时、即将销毁的对象,使用方法int &&right。意义就是避免内存操作
构造函数
构造函数就是一个类初始化的时候所需要的函数,主要分为以下几类
默认构造函数
带参数的构造函数
拷贝构造函数:可以实现对象的深拷贝和浅拷贝,省略&会导致无限递归
移动构造函数:接受一个右值引用(std::move)
转换构造函数
委托构造函数
class Base {public : int val; Base ():val (int (0 )) { std::cout << "默认构造函数. value is " << this ->val << std::endl; } Base (int v):val (v) { std::cout << "带参数的构造函数. value is " << this ->val << std::endl; } Base (const Base& other): val (other.val) { std::cout << "拷贝构造函数. value is " << this ->val << std::endl; } Base (Base&& other) noexcept : val (other.val) { other.val = 0xFF ; std::cout << "移动构造函数. value is " << this ->val << std::endl; } explicit Base (double v) : val(static_cast<int>(v)) { std::cout << "转换构造函数. value is " << this ->val << std::endl; } Base (char v) : Base () { this ->val = v; std::cout << "委托构造函数. value is " << this ->val << std::endl; } ~Base () {}; }; int main () { Base a; Base b (1 ) ; Base c (b) ; Base d (std::move(b)) ; Base e (3.124 ) ; Base f ('A' ) ; return 0 ; }