-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Coding Style
Elad Ashkenazi edited this page Oct 3, 2023
·
26 revisions
We recommend to follow these guidelines when writing code for RPCS3. They aren't very strict rules since we want to be flexible and we understand that under certain circumstances some of them can be counterproductive. Just try to follow as many of them as possible:
- Variable naming: lower_case_underscored
- Globals: g_*
- Class members: m_*
- Statics: s_*
- Template parameter names: CamelCase, or just T, U, V...
- Avoid
#defines
, use constant variables instead. - Put curly-brackets (
{
and}
) on the next line. - Try to eliminate all compiler warnings from your code.
- Try to use C++ standard data types whenever it's possible (e.g. std::string instead of QString).
- Comment every hack you do, every snippet you comment out and every improvable code.
- If you have to comment or place a commented code snippet, include the reasons to do that in the comment.
- Don't use
/**/
for commenting out multiple lines. Use//
on every line instead. In Visual Studio, for example, you can just select desired lines and useCtrl+K,C
combination to comment every line with//
,Ctrl+K,U
reverts this. - Ensure that every source file you modify has the newline at the end of file. Every line ends with "newline" and the end of file must have "newline" too, GitHub usually warns about it.
- Use brackets around multi-term ternary operator conditions especially if they occur with other code on the same line.
(x * y) + ((a > b)? c : d)
is more readable thanx * y + a > b? c : d
. - Use
ensure()
andfmt::throw_exception()
in order to add asserts in your code. - Avoid recursive locking, there are always better alternatives.
- Module functions and lv2 syscalls:
- Access files converting path with
vfs::get
function. - Return defined error codes. That is, use
return CELL_OK;
instead ofreturn 0;
. - Prefer the type
error_code
as the return value instead ofint
ors32
. (capable of reporting errors) - Use
named_thread
instead ofstd::thread
. To join it, callnamed_thread::operator()()
. - Use
g_fxo
for managing globally available variables for emulation, each variable must have a unique type.
- Access files converting path with
- Use only limited number of types as function arguments and result types.
- Use
s8
,s16
,s32
,s64
for signed integral types. These are aliases tostd::int8_t
,std::int16_t
,std::int32_t
,std::int64_t
respectively.s128
has been recently added and is a 128-bit signed integer. - Use
u8
,u16
,u32
,u64
for unsigned integral types. These are aliases tostd::uint8_t
,std::uint16_t
,std::uint32_t
,std::uint64_t
respectively.u128
has been recently added and is a 128-bit unsigned integer. - Use
f32
andf64
for floating point numbers. These are aliases tofloat
anddouble
. - Use
b8
instead ofbool
. This type is a fixed 8-bit boolean-mimicking type. - Use
char
for UTF-8 string characters, usually asvm::cptr<char>
. Don't treat char values as signed or unsigned numbers. - Function arguments and results use native endianness.
- Use
- Use
vm::ptr<>
arguments for PS3 memory pointers.- Pointer to the datatype
T
isvm::ptr<T>
. For example,void *buf
becomesvm::ptr<void> buf
. - Pointer to the datatype
const T
isvm::cptr<T>
. For example,const char *path
becomesvm::cptr<char> path
. - Pointer to the function
T(T1 arg1, ...)
isvm::ptr<T(T1 arg1, ...)>
. - The function may be defined as an alias
using func_name = T(T1 arg1, ...);
and used asvm::ptr<func_name>
. - Note that types
vm::ptr<u32>
andvm::ptr<be_t<u32>>
are equal, becausebe_t<>
template is implicitly applied invm::ptr<>
for basic types. You always work with big endian values in ps3 virtual memory, excepting some very rare cases. - Usual pointers (
vm::ptr
) are native-endian itself, but don't forget that dereferencing vm::ptr leads to the PS3 memory which uses big-endian values by default (if it's not known explicitly that some specific value is little-endian). - Pointers in PS3 memory must be big-endian: define them as
vm::bptr
orvm::bcptr
.
- Pointer to the datatype
- Allocate memory for temporary variables with
vm::var<>
(RAII based). It has similar semantics tovm::ptr<>
.- Because it is RAII based, prefer to use it as lvalue, do not leak its freed address by doing something like:
vm::ptr<u32> _u32 = vm::var<u32>{};
- Use
vm::gvar<>
to allocate memory to be used globally.
- Because it is RAII based, prefer to use it as lvalue, do not leak its freed address by doing something like:
- Don't forget logging at the top of every function. Print all its arguments with
%d
or0x%x
(always use0x%x
if not sure).- Don't forget
0x
in0x%x
. It may be really confusing. - Use
moduleName.todo()
and other associated methods. - Use
.todo()
method for unimplemented functions. - Use
.error()
method to print error inside of function that may require user's attention. For example, some file is not found or some emulation option is not set correctly. - Use
.succcess()
method for well-implemeneted, rarely-called but common functions. - Use
.warning()
method for partially implemented functions which still have some unimplemented functionality. - Use
.notice()
method to print debug information unconditionally. - Use
.trace()
method for well implemented and often-called functions. - Don't return CELL_OK and other error codes if the function result type doesn't mean error code.
- Don't forget
- Don't concatenate translated strings. Use
tr().arg()
instead.