Exception adalah alat yang kuat, tetapi bukan satu-satunya cara melaporkan kegagalan — dan tidak selalu yang paling pas. Untuk kegagalan yang memang diharapkan terjadi dalam pemakaian normal — berkas konfigurasi yang belum ada, kunci cache yang belum terisi — melempar exception terasa berlebihan: alurnya mahal, dan handler yang tersusun berlapis justru mengaburkan makna “ini hal biasa, segera pulihkan”. Untuk kebutuhan itu C++ menyediakan jalur kedua: std::error_code, sebuah objek kecil berisi kode dan kategori yang dibawa sebagai return value, tanpa unwinding dan tanpa handler. Chapter ini membedakannya, dimulai dari leluhurnya — errno — lalu berakhir pada overload <filesystem> yang kita singgung pada Chapter Error Handling File.

errno: Cara Lama yang Masih Ada #

Sebelum ada C++ modern, pustaka C melaporkan kegagalan lewat variabel global errno: function gagal mengembalikan nilai penanda (misalnya nullptr dari fopen), dan pemanggil membaca errno untuk mengetahui penyebabnya. Mekanisme ini masih hidup di C++ — <cerrno> memuatnya, dan banyak antarmuka C yang dipakai C++ tetap menggunakannya:

cpp
#include <cerrno>
#include <cstdio>
#include <cstring>
#include <filesystem>
#include <iostream>
#include <system_error>
namespace fs = std::filesystem;

int main() {
    // Cara 1 (lama): variabel global errno, diperiksa setelah call gagal.
    FILE* berkas = std::fopen("tidak-ada.txt", "r");
    if (berkas == nullptr) {
        std::cout << "errno: " << errno
                  << " -> " << std::strerror(errno) << "\n";
    }

    // Cara 2 (modern): overload dengan std::error_code, tanpa exception.
    std::error_code ec;
    auto ukuran = fs::file_size("tidak-ada.txt", ec);   // TIDAK melempar
    if (ec) {
        std::cout << "ec.value():   " << ec.value() << "\n";
        std::cout << "ec.message(): " << ec.message() << "\n";
        std::cout << "cocok errc:   "
                  << (ec == std::errc::no_such_file_or_directory) << "\n";
    }
    (void)ukuran;   // nilai tak berarti saat gagal; lihat ec saja

    // error_code juga bisa dibangun manual dari enumerasi std::errc.
    std::error_code ec2 = std::make_error_code(std::errc::invalid_argument);
    std::cout << "make_error_code: " << ec2.value() << " ("
              << ec2.message() << ")\n";
    return 0;
}

Output yang Dihasilkan:

text
errno: 2 -> No such file or directory
ec.value():   2
ec.message(): No such file or directory
cocok errc:   1
make_error_code: 22 (Invalid argument)

Cermati program ini:

  1. Baris 14-18 (jalur errno): fopen("tidak-ada.txt", "r") gagal mengembalikan nullptr, dan errno berisi 2 — kode POSIX untuk “No such file or directory” yang diterjemahkan std::strerror() menjadi teks.
  2. Baris 21-22 (overload dengan error_code): fs::file_size(path, ec) tidak pernah melempar exception; hasil kegagalan ditulis ke ec yang kita berikan. Ini overload yang sama yang dipakai pada Chapter Error Handling File untuk membaca ukuran berkas dengan aman.
  3. Baris 23-24 (if (ec)): error_code bisa dikonversi ke bool — true berarti terjadi kegagalan. Pola ini memanggil function, lalu mengecek laporannya, tanpa satu pun lompatan alur.
  4. Baris 27-28 (ec == std::errc::no_such_file_or_directory): dibandingkan kode angkanya, kita membandingkan enumerasi std::errc yang bermakna — bandingkan dengan menebak arti angka 2.
  5. Baris 32-33 (make_error_code): error_code juga bisa dibangun manual, berguna untuk melaporkan kegagalan dari kode kita sendiri dengan konvensi yang sama.

Anatomi std::error_code dan std::errc #

Objek std::error_code menampung dua informasi: nilai numerik (value()) dan kategori (category()) yang memberi arti pada nilai itu. Pemisahan ini penting karena angka yang sama bisa bermakna berbeda di dunia yang berbeda. Untuk kegagalan yang berasal dari sistem operasi, kategorinya adalah generic dan nilai-nilainya dipetakan ke enumerasi portabel std::errc — itulah mengapa perbandingan ec == std::errc::no_such_file_or_directory bekerja di Linux maupun Windows, meski kode mentahnya berbeda. Beberapa anggota std::errc yang paling sering dijumpai:

std::errcKondisi tipikal
no_such_file_or_directoryBerkas atau direktori tidak ada
permission_deniedIzin akses tidak mencukupi
invalid_argumentArgumen tidak sesuai bentuk yang diminta
file_existsTarget sudah ada saat harusnya belum
not_a_directoryOperasi direktori dijalankan ke berkas biasa
interruptedCall sistem terputus oleh sinyal

message() menyediakan teks siap-tampil seperti No such file or directory; teks ini berasal dari sistem operasi dan stabil di mesin yang sama, tetapi — seperti pesan what() yang dibahas pada Chapter Standard Exceptions — jadikan ia output untuk manusia, bukan data yang dibandingkan program.

Kapan error_code Lebih Tepat #

Kerangka keputusannya berputar pada satu pertanyaan: apakah kegagalan ini diharapkan? Bila ya — pemindai berkas yang melewati ribuan entri, cek cache yang boleh kosong, operasi yang boleh diulang — error_code (atau tipe hasil pada chapter berikutnya) menjaga alur tetap lurus: satu if, pulih, lanjut. Exception tetap alat yang benar untuk kegagalan luar biasa yang seharusnya jarang dan layak di-log. Ada juga pertimbangan teknis: melempar exception mahal, sedangkan membawa error_code hampir gratis; dan di beberapa domain — pustaka yang akan disematkan, codebase yang menonaktifkan exception — error_code adalah satu-satunya pilihan. <filesystem> menyediakan dua gaya pada hampir semua function-nya persis karena alasan ini: versi melempar untuk pemakaian umum, dan versi error_code untuk pemindaian massal serta kode yang alurnya panas.

Dengan dua jalur pelaporan — exception dan error code — pertanyaan “pakai yang mana” menjadi nyata. C++23 memberi jawaban yang elegan untuk kasus tengah: tipe yang membawa nilai ATAU kegagalan sebagai return value biasa, dengan alur yang bisa dirantai. Itulah std::expected, topik chapter berikutnya.

Referensi: cppreference — std::error_code , cppreference — std::errc , cppreference — errno .