This utility scans source files for predefined macros and generates a C/C++ compatible header file containing a corresponding enum. It is intended to automate the creation and maintenance of enumerated identifiers from a single source of truth: your code.
- Enum Generation: Creates an
enumfrom markers likeREGISTER_COUNTER(MyID). - Explicit Value Assignment: Optionally assign specific integer values to identifiers, e.g.,
REGISTER_COUNTER(MyID, 100). - C/C++ Compatibility: Generates a C++
enum classor a Ctypedef enumbased on the compiler (__cplusplusmacro). - Compile-Time Expansion: The registration macros expand directly to their enum values at compile time.
- Text-Based Configuration: All behavior is controlled through a
metacounterconfig.txtfile. - Duplicate Detection: Handles duplicate identifiers with a configurable policy:
ignore,warn, orerror. - Cross-Platform: Builds and runs on Windows, macOS, and Linux.
- A C compiler (GCC, Clang, or MSVC).
- On Windows, use a Developer Command Prompt for Visual Studio.
Run the build script for your platform. The executable will be placed in the bin/ directory.
- Linux / macOS:
./build.sh
- Windows:
build.bat
-
Configure
metacounterconfig.txt: Define the tool's inputs and outputs.# Path to the generated header. output_file: src/generated_counter_registry.h # ... other settings ...
-
Run the Tool: Execute the tool from the project root, passing the config file as an argument.
- Linux / macOS:
./bin/metacounter metacounterconfig.txt
- Windows:
bin\metacounter.exe metacounterconfig.txt
- Linux / macOS:
This example demonstrates the entire workflow by creating a basic profiler where each counter is an index into an array.
First, we create our Profiler. It depends on generated_counter_registry.h for the total number of counters (MAX_COUNT_INT) to correctly size its data array.
// src/core/Profiler.h
#pragma once
#include "../generated_counter_registry.h" // This file will be generated
#include <cstdint>
class Profiler {
public:
static void Increment(CounterID id) {
s_data[static_cast<int>(id)].call_count++;
}
private:
struct CounterData { uint64_t call_count; };
static CounterData s_data[MAX_COUNT_INT];
};Next, we add markers throughout the codebase to register the events we want to track. We can mix auto-incrementing counters with explicitly valued ones.
// src/engine/renderer.cpp
#include "../core/Profiler.h"
void Renderer::RenderFrame() {
Profiler::Increment(REGISTER_COUNTER(DrawCalls)); // Auto-assigned value 0
Profiler::Increment(REGISTER_COUNTER(ShaderBinds)); // Auto-assigned value 1
}
// src/game/player.cpp
#include "../core/Profiler.h"
void Player::Update() {
Profiler::Increment(REGISTER_COUNTER(PlayerHealth, 100)); // Explicit value
Profiler::Increment(REGISTER_COUNTER(PlayerStamina)); // Auto-assigned value 101
}Now, run the metacounter tool. It will process the markers from all source files and generate the following generated_counter_registry.h:
// THIS FILE IS AUTO-GENERATED BY METACOUNTER. DO NOT EDIT.
#pragma once
#ifdef __cplusplus
// C++ output
enum class CounterID {
DrawCalls = 0,
ShaderBinds = 1,
PlayerHealth = 100,
PlayerStamina = 101,
MAX_COUNT = 102
};
#define MAX_COUNT_INT 102
#define REGISTER_COUNTER(name, ...) CounterID::name
// ...
#else // Not C++
// C output
typedef enum {
CounterID_DrawCalls = 0,
CounterID_ShaderBinds = 1,
CounterID_PlayerHealth = 100,
CounterID_PlayerStamina = 101,
CounterID_MAX_COUNT = 102
} CounterID;
#define MAX_COUNT_INT 102
#define REGISTER_COUNTER(name, ...) CounterID_##name
// ...
#endif // __cplusplusWith the header generated, your project can now compile. The Profiler class has access to the MAX_COUNT_INT constant to size its array, and each REGISTER_COUNTER macro expands to its corresponding enum value at compile time.
The metacounter.txt file supports the following options:
| Key | Required | Description | Default |
|---|---|---|---|
output_file |
Yes | Path to the generated header file | - |
scan_ext |
Yes | Space-separated list of file extensions to scan | - |
enum_name |
No | Name of the generated enum | CounterID |
count_name |
No | Name of the count constant | MAX_COUNT |
marker_standard |
No | Macro name for standard registration | REGISTER_COUNTER |
marker_unique |
No | Macro name for unique registration | REGISTER_UNIQUE_COUNTER |
duplicate_policy |
No | How to handle duplicates: ignore, warn, or error |
ignore |
Source directories and files to scan are specified between begin_sources and end_sources markers.
This project is licensed under the MIT License.