/*
C++ で記述された、C 用の単一ファイル ヘッダーのみのライブ リロード ソリューション:
注: このリポジトリ内で重要なファイルはcr.h
のみです。
このファイルには、マークダウン形式のドキュメント、ライセンス、実装、およびパブリック API が含まれています。このリポジトリ内の他のファイルはすべてサポート ファイルなので、無視しても問題ありません。
vcpkg 依存関係マネージャーを使用して cr をダウンロードしてインストールできます。
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh
./vcpkg integrate install
./vcpkg install cr
vcpkg の cr ポートは、Microsoft チームのメンバーとコミュニティの貢献者によって最新の状態に保たれています。バージョンが古い場合は、vcpkg リポジトリで問題を作成するか、プル リクエストを作成してください。
(シン) ホスト アプリケーションの実行可能ファイルはcr
を利用して、動的にロード可能なバイナリの形式で実際のアプリケーションのライブ リロードを管理します。ホストは次のようになります。
#define CR_HOST // required in the host only and before including cr.h
#include "../cr.h"
int main ( int argc , char * argv []) {
// the host application should initalize a plugin with a context, a plugin
cr_plugin ctx ;
// the full path to the live-reloadable application
cr_plugin_open ( ctx , "c:/path/to/build/game.dll" );
// call the update function at any frequency matters to you, this will give
// the real application a chance to run
while (! cr_plugin_update ( ctx )) {
// do anything you need to do on host side (ie. windowing and input stuff?)
}
// at the end do not forget to cleanup the plugin context
cr_plugin_close ( ctx );
return 0 ;
}
ゲスト (実際のアプリケーション) は次のようになります。
CR_EXPORT int cr_main ( struct cr_plugin * ctx , enum cr_op operation ) {
assert ( ctx );
switch ( operation ) {
case CR_LOAD : return on_load (...); // loading back from a reload
case CR_UNLOAD : return on_unload (...); // preparing to a new reload
case CR_CLOSE : ...; // the plugin will close and not reload anymore
}
// CR_STEP
return on_update (...);
}
CR_INITIAL_FAILURE
を追加しました。最初のプラグインがクラッシュした場合、ホストは次のパスを決定する必要があり、壊れたプラグインはリロードされません。 cr_plugin_close
との一貫性を保つために、 cr_plugin_load
を非推奨にし、 cr_plugin_open
を優先しました。問題 #49 を参照してください。CR_BAD_IMAGE
を追加しました。これは、ファイルの生成 (コンパイラーまたはコピー) が遅い場合に発生する可能性があります。CR_BAD_IMAGE
参照)。現在、バージョンはクラッシュ ハンドラーで一度デクリメントされ、次にロールバック中にもう一度デクリメントされ、その後再びバンプされます。不完全なイメージによるロールバックでは、2 つのバージョンが誤ってロールバックされることはありません。イメージが有効になる (コピーまたはコンパイラーがイメージへの書き込みを終了する) まで、同じバージョンでロードを再試行し続けます。 CR_UNLOAD
中にversion
情報が使用される場合、バージョン情報は異なる値になるため、これはcr
の現在の使用に影響を与える可能性があります。2 つの簡単なサンプルがsamples
ディレクトリにあります。
1 つ目は、インスタンス間で動作するいくつかの基本的な静的状態と基本的なクラッシュ処理テストを示す単純なコンソール アプリケーションです。 Print to Output は、何が起こっているかを示すために使用されます。
2 つ目は、Dear ImGui を使用して opengl アプリケーションをライブリロードする方法を示しています。一部の状態はホスト側にありますが、コードの大部分はゲスト側にあります。
サンプルとテストでは fips ビルド システムを使用します。 Python と CMake が必要です。
$ ./fips build # will generate and build all artifacts
$ ./fips run crTest # To run tests
$ ./fips run imgui_host # To run imgui sample
# open a new console, then modify imgui_guest.cpp
$ ./fips make imgui_guest # to build and force imgui sample live reload
int (*cr_main)(struct cr_plugin *ctx, enum cr_op operation)
これは、動的にロード可能なバイナリ エントリ ポイント関数への関数ポインタです。
引数
ctx
現在ロードされているバージョン、失敗の理由、およびユーザー データに関する貴重な情報を含む、 host
からguest
に渡されるコンテキストへのポインター。詳細については、 cr_plugin
参照してください。operation
、 cr_op
参照してください。戻る
CR_USER
に設定されます。 0 またはhost
プロセスに渡される正の値。 bool cr_plugin_open(cr_plugin &ctx, const char *fullpath)
プラグインをロードして初期化します。
引数
ctx
プラグインの内部データとユーザーデータを管理するコンテキスト。fullpath
プラグインのロード可能なバイナリへのファイル名を含むフルパス、またはNULL
。戻る
true
、それ以外の場合はfalse
。 void cr_set_temporary_path(cr_plugin& ctx, const std::string &path)
プラグインの一時コピーが配置される一時パスを設定します。 cr_plugin_open()
の直後に呼び出す必要があります。 temporary
パスが設定されていない場合、ファイルの一時コピーは元のファイルが存在するのと同じディレクトリにコピーされます。
引数
ctx
プラグインの内部データとユーザーデータを管理するコンテキスト。path
プラグインの一時コピーを保存するために使用される既存のディレクトリへのフルパス。 int cr_plugin_update(cr_plugin &ctx, bool reloadCheck = true)
この関数はプラグインcr_main
関数を呼び出します。コア ロジック/アプリケーションが必要とするのと同じくらい頻繁に呼び出す必要があります。
引数
ctx
現在のプラグイン コンテキスト データ。reloadCheck
オプション: ディスク チェック (stat()) を実行して、ダイナミック ライブラリのリロードが必要かどうかを確認します。戻る
cr_main
から直接返されます。 void cr_plugin_close(cr_plugin &ctx)
プラグインが不要になったら、内部状態をクリーンアップします。
引数
ctx
現在のプラグイン コンテキスト データ。 cr_op
host
によって実行されているステップの種類を示す列挙型:
CR_LOAD
リロードによるロードが実行されており、保存された内部状態を復元するために使用できます。CR_STEP
アプリケーションの更新。これは通常の最も頻繁な操作です。CR_UNLOAD
プラグインを再ロードするためのアンロードが実行され、アプリケーションに必要なデータを保存する機会が 1 回与えられます。CR_CLOSE
プラグインを閉じるときに使用されます。これはCR_UNLOAD
と同様に機能しますが、その後CR_LOAD
期待されません。 cr_plugin
プラグイン インスタンスのコンテキスト構造体。
p
内部 cr データの不透明なポインタ。userdata
、リロード間で情報を渡すためにユーザーによって使用される場合があります。version
増分番号。最初のロードでは 1 から始まります。バージョンはクラッシュ処理プロセス中に変更されます。failure
、ロールバックの原因となった最後の障害エラー コードが保持されます。可能な値の詳細については、 cr_failure
を参照してください。 cr_failure
ロード可能なバイナリでクラッシュが発生した場合、クラッシュ ハンドラーは次のいずれかでクラッシュの理由を示します。
CR_NONE
エラーなし。CR_SEGFAULT
セグメンテーション違反。 Linux/OSX ではSIGSEGV
、Windows ではEXCEPTION_ACCESS_VIOLATION
。CR_ILLEGAL
不正な命令の場合。 Linux/OSX の場合はSIGILL
、Windows の場合はEXCEPTION_ILLEGAL_INSTRUCTION
です。CR_ABORT
中止、Linux/OSX ではSIGBRT
、Windows では使用されません。CR_MISALIGN
バス エラー、Linux/OSX ではSIGBUS
、Windows ではEXCEPTION_DATATYPE_MISALIGNMENT
。CR_BOUNDS
EXCEPTION_ARRAY_BOUNDS_EXCEEDED
です (Windows のみ)。CR_STACKOVERFLOW
はEXCEPTION_STACK_OVERFLOW
です (Windows のみ)。CR_STATE_INVALIDATED
静的なCR_STATE
管理の安全性エラー。CR_BAD_IMAGE
プラグインは有効なイメージではありません (つまり、コンパイラがまだ書き込んでいる可能性があります)。CR_OTHER
その他の信号、Linux のみ。CR_USER
ユーザー エラー ( cr_main
から返された負の値の場合)。 CR_HOST
定義この定義は、 cr.h
host
に含める前に使用する必要があります。 CR_HOST
が定義されていない場合、 cr.h
guest
実装で使用されるパブリック API ヘッダー ファイルとして機能します。
オプションで、自動静的状態管理 ( CR_STATE
) のsafety
動作モードを構成する方法として、 CR_HOST
次のいずれかの値に定義することもできます。
CR_SAFEST
リロード中に状態データ セクションのアドレスとサイズを検証します。何か変更があった場合、ロードはロールバックされます。CR_SAFE
状態セクションのサイズのみを検証します。これは、静的なアドレスが変更される可能性があることを意味します (静的なものへのポインターを保持しないことが最善です)。CR_UNSAFE
セクションのサイズが適合すること以外は何も検証しませんが、必ずしも正確である必要はありません (拡大は許容されますが、縮小は許容されません)。これがデフォルトの動作です。CR_DISABLE
自動静的状態管理を完全に無効にします。 CR_STATE
マクロリロード中に保存および復元されるグローバルまたはローカル静的変数にタグを付けるために使用されます。
使用法
static bool CR_STATE bInitialized = false;
cr.h をホスト (CR_HOST) に組み込む前にこれらのマクロを定義して、cr.h のメモリ割り当てやその他の動作をカスタマイズできます。
CR_MAIN_FUNC
: 「cr_main」シンボルをユーザー定義関数名に変更します。デフォルト: #define CR_MAIN_FUNC "cr_main"CR_ASSERT
: アサートをオーバーライドします。デフォルト: #define CA_ASSERT(e)assert(e)CR_REALLOC
: libc の realloc をオーバーライドします。デフォルト: #define CR_REALLOC(ptr, size) ::realloc(ptr, size)CR_MALLOC
: libc の malloc をオーバーライドします。デフォルト: #define CR_MALLOC(サイズ) ::malloc(サイズ)CR_FREE
: libc の free をオーバーライドします。デフォルト: #define CR_FREE(ptr) ::free(ptr)CR_DEBUG
: デバッグ メッセージを CR_ERROR、CR_LOG、CR_TRACE に出力します。CR_ERROR
: デバッグ メッセージを標準エラー出力に記録します。デフォルト (CR_DEBUG のみ): #define CR_ERROR(...) fprintf(stderr, VA_ARGS )CR_LOG
: デバッグ メッセージをログに記録します。デフォルト (CR_DEBUG のみ): #define CR_LOG(...) fprintf(stdout, VA_ARGS )CR_TRACE
: 関数呼び出しを出力します。デフォルト (CR_DEBUG のみ): #define CR_TRACE(...) fprintf(stdout, "CR_TRACE: %sn", FUNCTION )A: ここでこれを作成した理由について読んでください。
A: アプリケーション ホストと DLL の両方が動的ランタイム (/MD または /MDd) を使用していることを確認してください。ヒープ内に割り当てられたデータは、ゲストとの間でランタイムを共有することにより、同じアロケータ インスタンスで解放する必要があるためです。ホストでは、同じアロケータが使用されていることを保証します。
A: はい。これは Windows では問題なく動作するはずです。 Linux および OSX では、クラッシュ処理に問題が発生する可能性があります
何らかの理由でcr
の前に dll を読み込む必要がある場合、Visual Studio は PDB へのロックを保持したままになる可能性があります。この問題が発生している可能性があります。解決策はここにあります。
まず、ビルド システムが共有ライブラリにリンクしたままで干渉していないことを確認します。問題が発生する可能性があるものは非常に多く、共有ライブラリをcr
だけが処理できるようにする必要があります。 Linux では、何が起こっているかを見つける方法の詳細については、この問題を確認してください。
cr
C
リローダーであり、C を扱うと、単純なものがほとんど機能することが想定されます。
問題は、コード内で行われる変更の量に応じて、リンカがどのように再配置を決定するかということです。インクリメンタルな変更やローカライズされた変更については、何の問題もありませんでした。一般的に、通常の C コードを作成する場合には、ほとんど問題はありませんでした。現在、物事がより複雑になり、C++ に近づくと、よりリスクが高くなります。複雑な処理を行う必要がある場合は、RCCPP を確認し、この PDF とcr
に関する私の元のブログ投稿を読むことをお勧めします。
これらすべての情報をもとに、どちらが自分のユースケースに適しているかを判断できるようになります。
cr
スポンサーcr
の MacOSX への移植を後援するため。
ダニー・グレイン
ロカス・クプスティス
ノア・ラインハート
ニクラス・ランドバーグ
セファー・タグディシアン
ロバート・ガブリエル・ジャカボスキー
@pixelherodev
アレクサンダー
私たちはあらゆる貢献を歓迎します。小さなことでも貢献できます。たとえ 1 文字のタイプミスの修正であっても歓迎されます。
私たちが必要とするのは、徹底的にテストし、コード スタイルを維持し、ドキュメントを最新の状態に保つことだけです。
また、同じライセンスに基づいて投稿をリリースすることを受け入れ、同意します。
MIT ライセンス (MIT)
著作権 (c) 2017 ダニー アンジェロ カルミナティ グレイン
本ソフトウェアおよび関連ドキュメント ファイル (以下「ソフトウェア」) のコピーを入手した人には、使用、コピー、変更、マージする権利を含むがこれらに限定されない、制限なくソフトウェアを取り扱う許可が、ここに無償で与えられます。 、以下の条件を条件として、本ソフトウェアのコピーを出版、配布、サブライセンス、および/または販売すること、および本ソフトウェアが提供される人物にそのことを許可すること。
上記の著作権表示およびこの許可通知は、ソフトウェアのすべてのコピーまたは主要部分に含まれるものとします。
ソフトウェアは「現状のまま」提供され、明示的か黙示的かを問わず、商品性、特定目的への適合性、および非侵害の保証を含むがこれらに限定されない、いかなる種類の保証も行われません。いかなる場合においても、作者または著作権所有者は、契約行為、不法行為、またはその他の行為であるかどうかにかかわらず、ソフトウェアまたはソフトウェアの使用またはその他の取引に起因または関連して生じる、いかなる請求、損害、またはその他の責任に対しても責任を負わないものとします。ソフトウェア。
* /
#ifndef __CR_H__
#define __CR_H__
//
// Global OS specific defines/customizations
//
#if defined( _WIN32 )
#define CR_WINDOWS
#define CR_PLUGIN ( name ) "" name ".dll"
#elif defined( __linux__ )
#define CR_LINUX
#define CR_PLUGIN ( name ) "lib" name ".so"
#elif defined( __APPLE__ )
#define CR_OSX
#define CR_PLUGIN ( name ) "lib" name ".dylib"
#else
#error "Unknown/unsupported platform, please open an issue if you think this
platform should be supported."
#endif // CR_WINDOWS || CR_LINUX || CR_OSX
//
// Global compiler specific defines/customizations
//
#if defined( _MSC_VER )
#if defined( __cplusplus )
#define CR_EXPORT extern "C" __declspec(dllexport)
#define CR_IMPORT extern "C" __declspec(dllimport)
#else
#define CR_EXPORT __declspec(dllexport)
#define CR_IMPORT __declspec(dllimport)
#endif
#endif // defined(_MSC_VER)
#if defined( __GNUC__ ) // clang & gcc
#if defined( __cplusplus )
#define CR_EXPORT extern "C" __attribute__((visibility("default")))
#else
#define CR_EXPORT __attribute__((visibility("default")))
#endif
#define CR_IMPORT
#endif // defined(__GNUC__)
#if defined( __MINGW32__ )
#undef CR_EXPORT
#if defined( __cplusplus )
#define CR_EXPORT extern "C" __declspec(dllexport)
#else
#define CR_EXPORT __declspec(dllexport)
#endif
#endif
// cr_mode defines how much we validate global state transfer between
// instances. The default is CR_UNSAFE, you can choose another mode by
// defining CR_HOST, ie.: #define CR_HOST CR_SAFEST
enum cr_mode {
CR_SAFEST = 0 , // validate address and size of the state section, if
// anything changes the load will rollback
CR_SAFE = 1 , // validate only the size of the state section, this means
// that address is assumed to be safe if avoided keeping
// references to global/static states
CR_UNSAFE = 2 , // don't validate anything but that the size of the section
// fits, may not be identical though
CR_DISABLE = 3 // completely disable the auto state transfer
};
// cr_op is passed into the guest process to indicate the current operation
// happening so the process can manage its internal data if it needs.
enum cr_op {
CR_LOAD = 0 ,
CR_STEP = 1 ,
CR_UNLOAD = 2 ,
CR_CLOSE = 3 ,
};
enum cr_failure {
CR_NONE , // No error
CR_SEGFAULT , // SIGSEGV / EXCEPTION_ACCESS_VIOLATION
CR_ILLEGAL , // illegal instruction (SIGILL) / EXCEPTION_ILLEGAL_INSTRUCTION
CR_ABORT , // abort (SIGBRT)
CR_MISALIGN , // bus error (SIGBUS) / EXCEPTION_DATATYPE_MISALIGNMENT
CR_BOUNDS , // EXCEPTION_ARRAY_BOUNDS_EXCEEDED
CR_STACKOVERFLOW , // EXCEPTION_STACK_OVERFLOW
CR_STATE_INVALIDATED , // one or more global data section changed and does
// not safely match basically a failure of
// cr_plugin_validate_sections
CR_BAD_IMAGE , // The binary is not valid - compiler is still writing it
CR_INITIAL_FAILURE , // Plugin version 1 crashed, cannot rollback
CR_OTHER , // Unknown or other signal,
CR_USER = 0x100 ,
};
struct cr_plugin ;
typedef int ( * cr_plugin_main_func )( struct cr_plugin * ctx , enum cr_op operation );
// public interface for the plugin context, this has some user facing
// variables that may be used to manage reload feedback.
// - userdata may be used by the user to pass information between reloads
// - version is the reload counter (after loading the first instance it will
// be 1, not 0)
// - failure is the (platform specific) last error code for any crash that may
// happen to cause a rollback reload used by the crash protection system
struct cr_plugin {
void * p ;
void * userdata ;
unsigned int version ;
enum cr_failure failure ;
unsigned int next_version ;
unsigned int last_working_version ;
};
#ifndef CR_HOST
// Guest specific compiler defines/customizations
#if defined( _MSC_VER )
#pragma section(".state", read, write)
#define CR_STATE __declspec(allocate(".state"))
#endif // defined(_MSC_VER)
#if defined( CR_OSX )
#define CR_STATE __attribute__((used, section("__DATA,__state")))
#else
#if defined( __GNUC__ ) // clang & gcc
#define CR_STATE __attribute__((section(".state")))
#endif // defined(__GNUC__)
#endif
#else // #ifndef CR_HOST
// Overridable macros
#ifndef CR_LOG
# ifdef CR_DEBUG
# include
# define CR_LOG (...) fprintf(stdout, __VA_ARGS__)
# else
# define CR_LOG (...)
# endif
#endif
#ifndef CR_ERROR
# ifdef CR_DEBUG
# include
# define CR_ERROR (...) fprintf(stderr, __VA_ARGS__)
# else
# define CR_ERROR (...)
# endif
#endif
#ifndef CR_TRACE
# ifdef CR_DEBUG
# include
# define CR_TRACE fprintf(stdout, "CR_TRACE: %sn", __FUNCTION__);
# else
# define CR_TRACE
# endif
#endif
#ifndef CR_MAIN_FUNC
# define CR_MAIN_FUNC "cr_main"
#endif
#ifndef CR_ASSERT
# include
# define CR_ASSERT ( e ) assert(e)
#endif
#ifndef CR_REALLOC
# include
# define CR_REALLOC ( ptr , size ) ::realloc(ptr, size)
#endif
#ifndef CR_FREE
# include
# define CR_FREE ( ptr ) ::free(ptr)
#endif
#ifndef CR_MALLOC
# include
# define CR_MALLOC ( size ) ::malloc(size)
#endif
#if defined( _MSC_VER )
// we should probably push and pop this
# pragma warning(disable:4003) // not enough actual parameters for macro 'identifier'
#endif
#define CR_DO_EXPAND ( x ) x##1337
#define CR_EXPAND ( x ) CR_DO_EXPAND(x)
#if CR_EXPAND ( CR_HOST ) == 1337
#define CR_OP_MODE CR_UNSAFE
#else
#define CR_OP_MODE CR_HOST
#endif
#include
#include // duration for sleep
#include // memcpy
#include
#include // this_thread::sleep_for
#if defined( CR_WINDOWS )
#define CR_PATH_SEPARATOR '\'
#define CR_PATH_SEPARATOR_INVALID '/'
#else
#define CR_PATH_SEPARATOR '/'
#define CR_PATH_SEPARATOR_INVALID '\'
#endif
static void cr_split_path ( std :: string path , std :: string & parent_dir ,
std :: string & base_name , std :: string & ext ) {
std:: replace ( path . begin (), path . end (), CR_PATH_SEPARATOR_INVALID ,
CR_PATH_SEPARATOR );
auto sep_pos = path . rfind ( CR_PATH_SEPARATOR );
auto dot_pos = path . rfind ( '.' );
if ( sep_pos == std :: string :: npos ) {
parent_dir = "" ;
if ( dot_pos == std :: string :: npos ) {
ext = "" ;
base_name = path ;
} else {
ext = path . substr ( dot_pos );
base_name = path . substr ( 0 , dot_pos );
}
} else {
parent_dir = path . substr ( 0 , sep_pos + 1 );
if ( dot_pos == std :: string :: npos || sep_pos > dot_pos ) {
ext = "" ;
base_name = path . substr ( sep_pos + 1 );
} else {
ext = path . substr ( dot_pos );
base_name = path . substr ( sep_pos + 1 , dot_pos - sep_pos - 1 );
}
}
}
static std :: string cr_version_path ( const std :: string & basepath ,
unsigned version ,
const std :: string & temppath ) {
std:: string folder , fname , ext ;
cr_split_path ( basepath , folder , fname , ext );
std:: string ver = std :: to_string ( version );
#if defined( _MSC_VER )
// When patching PDB file path in library file we will drop path and leave only file name.
// Length of path is extra space for version number. Trim file name only if version number
// length exceeds pdb folder path length. This is not relevant on other platforms.
if ( ver . size () > folder . size ()) {
fname = fname . substr ( 0 , fname . size () - ( ver . size () - folder . size () - 1 ));
}
#endif
if (! temppath . empty ()) {
folder = temppath ;
}
return folder + fname + ver + ext ;
}
namespace cr_plugin_section_type {
enum e { state , bss , count };
}
namespace cr_plugin_section_version {
enum e { backup , current , count };
}
struct cr_plugin_section {
cr_plugin_section_type :: e type = {};
intptr_t base = 0 ;
char * ptr = 0 ;
int64_t size = 0 ;
void * data = nullptr ;
};
struct cr_plugin_segment {
char * ptr = 0 ;
int64_t size = 0 ;
};
// keep track of some internal state about the plugin, should not be messed
// with by user
struct cr_internal {
std :: string fullname = {};
std:: string temppath = {};
time_t timestamp = {};
void * handle = nullptr ;
cr_plugin_main_func main = nullptr ;
cr_plugin_segment seg = {};
cr_plugin_section data [ cr_plugin_section_type :: count ]
[ cr_plugin_section_version :: count ] = {};
cr_mode mode = CR_SAFEST ;
};
static bool cr_plugin_section_validate ( cr_plugin & ctx ,
cr_plugin_section_type :: e type ,
intptr_t vaddr , intptr_t ptr ,
int64_t size );
static void cr_plugin_sections_reload ( cr_plugin & ctx ,
cr_plugin_section_version :: e version );
static void cr_plugin_sections_store ( cr_plugin & ctx );
static void cr_plugin_sections_backup ( cr_plugin & ctx );
static void cr_plugin_reload ( cr_plugin & ctx );
static int cr_plugin_unload ( cr_plugin & ctx , bool rollback , bool close );
static bool cr_plugin_changed ( cr_plugin & ctx );
static bool cr_plugin_rollback ( cr_plugin & ctx );
static int cr_plugin_main ( cr_plugin & ctx , cr_op operation );
void cr_set_temporary_path ( cr_plugin & ctx , const std :: string & path ) {
auto pimpl = ( cr_internal * ) ctx . p ;
pimpl -> temppath = path ;
}
#if defined( CR_WINDOWS )
// clang-format off
#ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
#endif
#include
#include
// clang-format on
#if defined( _MSC_VER )
#pragma comment(lib, "dbghelp.lib")
#endif
using so_handle = HMODULE ;
#ifdef UNICODE
# define CR_WINDOWS_ConvertPath ( _newpath , _path ) std::wstring _newpath(cr_utf8_to_wstring(_path))
static std :: wstring cr_utf8_to_wstring ( const std :: string & str ) {
int wlen = MultiByteToWideChar ( CP_UTF8 , 0 , str . c_str (), -1 , 0 , 0 );
wchar_t wpath_small [ MAX_PATH ];
std:: unique_ptr < wchar_t [] > wpath_big ;
wchar_t * wpath = wpath_small ;
if ( wlen > _countof ( wpath_small )) {
wpath_big = std :: unique_ptr < wchar_t [] > ( new wchar_t [ wlen ]);
wpath = wpath_big . get ();
}
if ( MultiByteToWideChar ( CP_UTF8 , 0 , str . c_str (), -1 , wpath , wlen ) != wlen ) {
return L"" ;
}
return wpath ;
}
#else
# define CR_WINDOWS_ConvertPath ( _newpath , _path ) const std::string &_newpath = _path
#endif // UNICODE
static time_t cr_last_write_time ( const std :: string & path ) {
CR_WINDOWS_ConvertPath ( _path , path );
WIN32_FILE_ATTRIBUTE_DATA fad ;
if (! GetFileAttributesEx ( _path . c_str (), GetFileExInfoStandard , & fad )) {
return -1 ;
}
if ( fad . nFileSizeHigh == 0 && fad . nFileSizeLow == 0 ) {
return -1 ;
}
LARGE_INTEGER time ;
time . HighPart = fad . ftLastWriteTime . dwHighDateTime ;
time . LowPart = fad . ftLastWriteTime . dwLowDateTime ;
return static_cast < time_t > ( time . QuadPart / 10000000 - 11644473600LL );
}
static bool cr_exists ( const std :: string & path ) {
CR_WINDOWS_ConvertPath ( _path , path );
return GetFileAttributes ( _path . c_str ()) != INVALID_FILE_ATTRIBUTES ;
}
static bool cr_copy ( const std :: string & from , const std :: string & to ) {
CR_WINDOWS_ConvertPath ( _from , from );
CR_WINDOWS_ConvertPath ( _to , to );
return CopyFile ( _from . c_str (), _to . c_str (), FALSE) ? true : false;
}
static void cr_del ( const std :: string & path ) {
CR_WINDOWS_ConvertPath ( _path , path );
DeleteFile ( _path . c_str ());
}
// If using Microsoft Visual C/C++ compiler we need to do some workaround the
// fact that the compiled binary has a fullpath to the PDB hardcoded inside
// it. This causes a lot of headaches when trying compile while debugging as
// the referenced PDB will be locked by the debugger.
// To solve this problem, we patch the binary to rename the PDB to something
// we know will be unique to our in-flight instance, so when debugging it will
// lock this unique PDB and the compiler will be able to overwrite the
// original one.
#if defined( _MSC_VER )
#include
#include
#include
#include
static std :: string cr_replace_extension ( const std :: string & filepath ,
const std :: string & ext ) {
std:: string folder , filename , old_ext ;
cr_split_path ( filepath , folder , filename , old_ext );
return folder + filename + ext ;
}
template < class T >
static T struct_cast ( void * ptr , LONG offset = 0 ) {
return reinterpret_cast < T > ( reinterpret_cast < intptr_t > ( ptr ) + offset );
}
// RSDS Debug Information for PDB files
using DebugInfoSignature = DWORD ;
#define CR_RSDS_SIGNATURE 'SDSR'
struct cr_rsds_hdr {
DebugInfoSignature signature ;
GUID guid ;
long version ;
char filename [ 1 ];
};
static bool cr_pe_debugdir_rva ( PIMAGE_OPTIONAL_HEADER optionalHeader ,
DWORD & debugDirRva , DWORD & debugDirSize ) {
if ( optionalHeader -> Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC ) {
auto optionalHeader64 =
struct_cast < PIMAGE_OPTIONAL_HEADER64 > ( optionalHeader );
debugDirRva =
optionalHeader64 -> DataDirectory [ IMAGE_DIRECTORY_ENTRY_DEBUG ]
. VirtualAddress ;
debugDirSize =
optionalHeader64 -> DataDirectory [ IMAGE_DIRECTORY_ENTRY_DEBUG ]. Size ;
} else {
auto optionalHeader32 =
struct_cast < PIMAGE_OPTIONAL_HEADER32 > ( optionalHeader );
debugDirRva =
optionalHeader32 -> DataDirectory [ IMAGE_DIRECTO