Coding Conventions
An overview of the stylistic conventions used in the codebase
About this Document
This is a reference, not an explanation. For the rationale behind these conventions, see RFC: Code Conventions
Lua Conventions
There's only a few things that are somewhat important to me (I think):
- Lua-C bindings implemented by external libraries generally use
snake_case
(mostly because it can't be changed)- Example:
webview.eval()
- Example:
- Lua libraries written for the runtime itself should be written in
camelCase
, with lower-case module names- Example:
bdd.startTestRunner()
- Example:
- Lua-C++ bindings written for the runtime itself should be written in
PascalCase
, and prefixed withC_*
- Example:
C_FileSystem.ReadFile()
- Example:
- The standard Lua module syntax (return table of exports) is used for every module and library
- OOP constructors are called
Construct
but never used explicitly (as inlocal myObject = Object:Construct()
)- Instead, I decided to use a Pythonesque/early-JS-style function call convention:
local myObject = Object()
- Instead, I decided to use a Pythonesque/early-JS-style function call convention:
- Constants and enum values are written in
SHOUTING_CASE
(yes, I made that term up)
It can be argued that combining multiple styles is confusing (and it may be so), but for the time being that's how it is.
Lua Example: Object-Oriented (Class Module)
The following code implements a Lua class module that should help illustrate the code conventions:
-- Modules, classes, and high-level APIs use PascalCase
local MyExampleClass = {}
-- Constructor function that will be hidden via __call metatable magic
-- The colon (:) symbol is required for OOP code so that self is passed as the first parameter
function MyExampleClass:Construct()
local instance = {}
setmetatable(instance, self) -- Sets up the inheritance hierarchy
return instance
end
-- This allows instantiation like so: local myExampleInstance = MyExampleClass()
MyExampleClass.__call = MyExampleClass.Construct
setmetatable(MyExampleClass, MyExampleClass)
-- Functions that aren't part of the exported API use camelCase
local function standaloneFunctionThatIsNotExported()
local someLocalVariable = 42 -- Variables use camelCase, as well
end
-- Module-level functions use PascalCase, too
function MyExampleClass:DemonstrateBindingsUsage()
-- Low level APIs use snake_case (for better or worse. It's out of my hands...)
-- They are direct LuaJIT-FFI bindings or Lua-C-bindings, and expose cdata (or userdata)
local someLowLevelBinding = require("binding")
someLowLevelBinding.some_c_api_called_from_lua()
-- High-level APIs use PascalCase, regardless of whether they're implemented in Lua or C++
-- A high-level API is one that abstracts C/FFI details away for convenience and safety
-- They may be implemented in Lua or C++ depending on performance requirements
C_HighLevelAPI.DoSomething() -- Assumed to be globally available here
-- Finally, there's non-OOP libraries written in 100% Lua which don't deal with native code
-- They generally use camelCase naming (just like variables and independent functions)
local myLuaModule = require("luamodule")
myLuaModule.doSomethingCool()
end
-- Lua modules always return themselves (as a table of exports)
return MyExampleClass
Needless to say, the implementation details are irrelevant here and should be completely ignored.
C++ Conventions
The conventions are the same as in Lua. Except everything's low-level code, but I don't want snake_case
everywhere. To be honest, I haven't decided on a best approach for native code yet as I don't have enough experience with it. For now, all code should use the same style for both languages. Default to PascalCase
instead of snake_case
however.
Beyond that, there are a few things I've been trying to do consistently:
- Standalone functions, just like variables, use
camelCase
every time - Member variables are prefixed, e.g.,
m_someLocalMemberVariable
(even if I find it bizarre) - Global variables aren't used, so there's no
g_thisShouldNeverHappen
anywhere - Namespaces use
PascalCase
, such asThisDoesReadNicely::SomeFunctionNameGoesHere()
- Speaking of which: Function names for namespaces and classes also use
PascalCase
(as above) - I also try to avoid preprocessor instructions (especially
#define
) and useconst
values instead
There's probably a lot more to be considered, but I'm not a C++ programmer. If you have any ideas, let me know!
Formatting
These guidelines only apply to things that require human interaction. Formatting conventions are handled by clang-format
and stylua
, precisely so that no thought needs to be wasted on them beyond what was needed to initially set up some rules. If there's a good case to be made for changing the formatting, I'm all ears (except for tabs vs. spaces).
Seriously, anyone who proposes using spaces will be fed to my pet hamster. No, I don't actually have a pet hamster. Yet.
Disclaimer
These are not necessarily the best (or even good) conventions. In fact, they may seem quite weird. This is mostly just a description of what I've been using. Thinking at different levels of abstraction is necessary when dealing with native code and Lua, as well as FFI or regular C-API bindings, so that's why they're based on proximity to low-level code.
As for C++ conventions, I really have no idea what I'm doing here. So I mostly just made it so that the code looked nice.