Project 3: Compile-Time Unit Conversion Library

Encode physical units in the type system so invalid math fails at compile time.

Quick Reference

Attribute Value
Difficulty Expert
Time Estimate 2-3 weeks
Main Programming Language C++17
Coolness Level Pure Magic
Business Potential Resume Gold
Prerequisites Templates, type traits, static_assert
Key Topics TMP, std::ratio, dimensional analysis

1. Learning Objectives

  1. Represent dimensions at compile time.
  2. Use template metaprogramming to compute unit arithmetic.
  3. Provide clear compile-time errors for invalid operations.
  4. Build a zero-runtime-cost safety layer.

2. All Theory Needed (Per-Concept Breakdown)

2.1 Template Metaprogramming Basics

Description

TMP lets you compute types and constants during compilation. Your unit system is purely compile-time.

Definitions & Key Terms

  • Instantiation: compiler generates code from templates.
  • Type trait: compile-time query.

Mental Model Diagram (ASCII)

Template + Types -> Compiler -> Generated Types

How It Works

  1. Templates are expanded during compile time.
  2. Errors show up as type mismatches.

Minimal Concrete Example

template<typename T> struct is_length : std::false_type {};

Common Misconceptions

  • “TMP is too slow” -> correct design keeps compile time manageable.

Check-Your-Understanding Questions

  • Why is SFINAE useful for constraints?

Where You’ll Apply It

  • All operators in this project

2.2 Dimensional Analysis

Description

Physical equations must be dimensionally consistent. Encoding dimensions in types enforces that rule.

Definitions & Key Terms

  • Base dimension: mass, length, time, etc.
  • Exponent vector: type-level exponents for each base.

Mental Model Diagram (ASCII)

Meters:   (0,1,0)
Seconds:  (0,0,1)
Velocity: (0,1,-1)

Minimal Concrete Example

template<int M, int L, int T> struct Dim {};

Common Misconceptions

  • “Units only matter at runtime” -> they can be enforced by the compiler.

Where You’ll Apply It

  • Unit multiplication/division

2.3 std::ratio and Scaling

Description

std::ratio encodes rational numbers at compile time and lets you express prefixes like kilo or milli.

Definitions & Key Terms

  • Ratio: compile-time fraction (1/1000).

Mental Model Diagram (ASCII)

km = ratio<1000,1>
mm = ratio<1,1000>

Minimal Concrete Example

using kilo = std::ratio<1000,1>;

Common Misconceptions

  • “Ratios cost runtime” -> they are compile-time constants.

3. Project Specification

3.1 What You Will Build

A small unit library that supports base units, derived units, scaling factors, and operator overloads with compile-time validation.

3.2 Functional Requirements

  1. Base dimensions and derived units.
  2. Operator + only for identical units.
  3. Operator * and / create new dimensions.
  4. Scaling with std::ratio (kilo, milli).

3.3 Non-Functional Requirements

  • Zero runtime overhead for unit checks.
  • Clear compiler errors.

3.4 Example Usage / Output

Meters d(10.0);
Seconds t(2.0);
auto v = d / t; // Velocity
std::cout << v.count() << " m/s\n";

3.5 Data Formats / Schemas / Protocols

  • Type-level dimension struct: Dim<M,L,T>
  • Value wrapper: Value<Dim, Ratio>

3.6 Edge Cases

  • Adding incompatible units
  • Dividing by zero (runtime)

3.7 Real World Outcome

3.7.1 How to Run

cmake -S . -B build
cmake --build build
./build/units_demo

3.7.2 Golden Path Demo

10 m/s

3.7.3 Failure Demo

error: static assertion failed: Cannot add values with different dimensions

4. Solution Architecture

4.1 High-Level Design

[Dim<M,L,T>] + [Ratio] -> Value<Dim, Ratio>

4.2 Key Components

| Component | Responsibility | Key Decisions | |—|—|—| | Dim | exponent vector | fixed ordering of bases | | Value | runtime value + unit | strong type safety | | Operators | dimension arithmetic | compile-time checks |

4.3 Data Structures

template<typename Dim, typename Ratio = std::ratio<1>>
struct Value { double v; };

4.4 Algorithm Overview

  • Multiply: add exponents
  • Divide: subtract exponents

5. Implementation Guide

5.1 Development Environment Setup

cmake -S . -B build
cmake --build build

5.2 Project Structure

units/
├── src/units.hpp
├── src/units.cpp
├── tests/units_tests.cpp
└── CMakeLists.txt

5.3 The Core Question You’re Answering

“Can the compiler prevent incorrect equations before the program runs?”

5.4 Concepts You Must Understand First

  • TMP and SFINAE
  • std::ratio
  • static_assert

5.5 Questions to Guide Your Design

  • How will you encode base units?
  • Where will you perform exponent arithmetic?
  • How will you display units in output?

5.6 Thinking Exercise

Compute the dimension vector for acceleration and force.

5.7 The Interview Questions They’ll Ask

  1. What is SFINAE?
  2. How does static_assert help API design?
  3. Why are zero-cost abstractions important?

5.8 Hints in Layers

Hint 1: Represent dimensions as int template parameters. Hint 2: Use helper templates for dimension arithmetic. Hint 3: Use static_assert for friendly errors.

5.9 Books That Will Help

| Topic | Book | Chapter | |—|—|—| | TMP | C++ Templates | TMP chapters | | Type system | The C++ Programming Language | Templates chapter | | Generics | A Tour of C++ | Generics section |

5.10 Implementation Phases

Phase 1: Base Units (3-4 days)

  • Define Dim and Value.
  • Checkpoint: compile and run simple units.

Phase 2: Operators (4-5 days)

  • Implement +, -, *, /.
  • Checkpoint: invalid adds fail.

Phase 3: Scaling and Polishing (3-4 days)

  • Add std::ratio scaling.
  • Checkpoint: km to m conversions.

5.11 Key Implementation Decisions

| Decision | Options | Recommendation | Rationale | |—|—|—|—| | Dimension encoding | int pack | int pack | simple, fast compile | | Error handling | SFINAE / static_assert | static_assert | clearer messages |


6. Testing Strategy

6.1 Test Categories

| Category | Purpose | Examples | |—|—|—| | Compile-time | invalid ops | meters + seconds fails | | Runtime | numeric output | velocity calc |

6.2 Critical Test Cases

  1. meters + seconds fails to compile.
  2. meters / seconds yields velocity.

7. Common Pitfalls & Debugging

7.1 Frequent Mistakes

| Pitfall | Symptom | Solution | |—|—|—| | Bad error messages | huge template dump | use static_assert | | Wrong exponent math | incorrect derived units | unit tests |


8. Extensions & Challenges

8.1 Beginner Extensions

  • Add more base dimensions (current, temperature).

8.2 Intermediate Extensions

  • Add user-defined literals (10.0_m).

8.3 Advanced Extensions

  • Integrate with std::chrono or Boost.Units.

9. Real-World Connections

9.1 Industry Applications

  • Physics engines
  • Scientific computing

10. Resources

10.1 Essential Reading

  • C++ Templates (TMP chapters)
  • The C++ Programming Language (templates)

11. Self-Assessment Checklist

  • I can encode base units as types.
  • I can explain why invalid units fail to compile.

12. Submission / Completion Criteria

Minimum Viable Completion:

  • Base units + operator checks

Full Completion:

  • Scaling support with std::ratio

Excellence:

  • User-defined literals and derived-unit aliases