Skip to content

leiapollos/metacounter

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Metacounter

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.

Core Functionality

  • Enum Generation: Creates an enum from markers like REGISTER_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 class or a C typedef enum based on the compiler (__cplusplus macro).
  • 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.txt file.
  • Duplicate Detection: Handles duplicate identifiers with a configurable policy: ignore, warn, or error.
  • Cross-Platform: Builds and runs on Windows, macOS, and Linux.

Build Instructions

Prerequisites

  • A C compiler (GCC, Clang, or MSVC).
  • On Windows, use a Developer Command Prompt for Visual Studio.

Compilation

Run the build script for your platform. The executable will be placed in the bin/ directory.

  • Linux / macOS:
    ./build.sh
  • Windows:
    build.bat

Usage

  1. Configure metacounterconfig.txt: Define the tool's inputs and outputs.

    # Path to the generated header.
    output_file: src/generated_counter_registry.h
    # ... other settings ...
  2. 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

Example: Building a Simple Profiler

This example demonstrates the entire workflow by creating a basic profiler where each counter is an index into an array.

Step 1: Define the Profiler System

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];
};

Step 2: Register Counters in Your Code

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
}

Step 3: Generate the Header

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 // __cplusplus

Step 4: Compile Your Project

With 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.

Configuration Reference

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.

License

This project is licensed under the MIT License.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors