ABI Uyumluluğu
Bir şehirdeki elektrik prizi standardını düşün. Türkiye'de F tipi priz kullanılır — iki yuvarlak delikli. Almanya'dan aldığın bir cihazın fişi bu prize uyar çünkü aynı standardı kullanırlar. Ama İngiltere'den aldığın cihaz? Üç dikdörtgen pinli G tipi fiş. Cihazın kendisi mükemmel çalışıyor, Türk prizi de mükemmel çalışıyor — ama birbirine fiziksel olarak uymuyorlar. Adaptör lazım. İşte ABI uyumluluğu tam olarak bu: iki binary'nin (derlenmiş kodun) birbirleriyle fiziksel düzeyde konuşabilmesi. Kaynak kodlar uyumlu olabilir ama derlenmiş halleri uyumsuz olabilir.
ABI konusu, özellikle kütüphane geliştiricileri ve uzun ömürlü projeler için kritiktir. Bir .so veya .dll dosyası yayınlıyorsan, kullanıcıların her güncelleme sonrası yeniden derlemesini gerektirmeden güncelleme yapabilmek istiyorsun. Bu da ABI'yi anlamayı zorunlu kılıyor.
ABI Nedir?
ABI (Application Binary Interface), derlenmiş kodun (binary) düzeyindeki arayüzdür. Şunları tanımlar:
Fonksiyon çağrı konvansiyonu: Argümanlar registerlara mı yoksa stack'e mi konulur? Dönüş değeri nereye yazılır?
Veri yapısı layout: Struct üyeleri bellekte nasıl sıralanır? Padding nasıl eklenir?
Name mangling: C++ fonksiyon isimleri binary'de nasıl temsil edilir?
Vtable düzeni: Sanal fonksiyon tablosu nasıl organize edilir?
Exception handling mekanizması: Exception bilgisi nasıl taşınır?
Temel tiplerin boyutu ve hizalaması:
int,long, pointer boyutları
ABI vs API: Kritik Fark
| API | ABI | |
|---|---|---|
| Seviye | Kaynak kod | Binary (derlenmiş kod) |
| Ne tanımlar? | Fonksiyon imzaları, tip isimleri | Bellek düzeni, çağrı konvansiyonu |
| Uyumluluk kontrolü | Derleme zamanında (derleyici) | Yükleme/çalışma zamanında (linker/loader) |
| Kırıldığında ne olur? | Derleme hatası | Crash, veri bozulması, sessiz hata |
| Örnek | void foo(int x) → void foo(int x, int y) | Struct'a yeni üye eklemek |
API kırıldığında derleyici seni uyarır. ABI kırıldığında hiçbir uyarı almadan crash edersin. Bu yüzden ABI daha tehlikelidir.
// API değişikliği — derleyici yakalar
// Eski: void process(int x);
// Yeni: void process(int x, int y);
// Kullanıcı: process(5); → DERLEME HATASI (güvenli!)
// ABI değişikliği — derleyici YAKALAMAZ
// Kütüphane header:
struct Config {
int width;
int height;
// Yeni versiyon: bool fullscreen; EKLENDI
};
// Kullanıcı eski header ile derledi → sizeof(Config) farklı → CRASH!Name Mangling
C++ fonksiyon overloading destekler: aynı isimde farklı parametre listeli fonksiyonlar olabilir. Ama linker düzeyinde her sembol benzersiz olmalı. Bu sorunu çözen mekanizma name mangling'dir.
// Kaynak kod
namespace math {
int add(int a, int b);
double add(double a, double b);
int add(int a, int b, int c);
}
class Calculator {
public:
int compute(int x);
int compute(int x, int y);
};Derleyici bu fonksiyonları benzersiz isimlere dönüştürür. GCC/Clang'da (Itanium ABI):
| Fonksiyon | Mangled İsim |
|---|---|
math::add(int, int) | _ZN4math3addEii |
math::add(double, double) | _ZN4math3addEdd |
math::add(int, int, int) | _ZN4math3addEiii |
Calculator::compute(int) | _ZN10Calculator7computeEi |
Calculator::compute(int, int) | _ZN10Calculator7computeEii |
MSVC tamamen farklı bir mangling şeması kullanır. Bu, GCC ile derlenen bir kütüphanenin MSVC ile derlenen bir programla linklenemeyeceği anlamına gelir — aynı platform, aynı işlemci olsa bile.
Mangled İsimleri Görmek
# Derlenmiş binary'deki sembolleri göster
nm -C my_library.so # -C: demangle et (okunabilir)
nm my_library.so # Mangled haliyle göster
# Tek bir ismi demangle et
echo "_ZN4math3addEii" | c++filt
# Çıktı: math::add(int, int)
# Bir ismi mangle et (derleyiciye sor)
echo 'int math::add(int, int)' | g++ -x c++ - -S -o - 2>/dev/null | grep _ZNABI Kırılma Nedenleri
1. Struct/Class Layout Değişikliği
// Versiyon 1.0
struct Packet {
uint32_t id; // offset 0
uint16_t type; // offset 4
uint16_t flags; // offset 6
}; // sizeof = 8
// Versiyon 1.1 — ABI KIRILDI!
struct Packet {
uint32_t id; // offset 0
uint32_t sequence; // YENI — offset 4 ← type ve flags kaydı!
uint16_t type; // offset 8 (eskiden 4 idi!)
uint16_t flags; // offset 10 (eskiden 6 idi!)
}; // sizeof = 12
// Eski kodla derlenen program: packet.type'a offset 4'ten erişir
// Ama yeni struct'ta offset 4'te "sequence" var!
// Sonuç: yanlış veri okunur, crash veya sessiz veri bozulması2. Vtable Değişikliği
// Versiyon 1.0
class Shape {
public:
virtual double area() = 0; // vtable[0]
virtual double perimeter() = 0; // vtable[1]
virtual ~Shape() = default; // vtable[2]
};
// Versiyon 1.1 — ABI KIRILDI!
class Shape {
public:
virtual std::string name() = 0; // vtable[0] — YENI!
virtual double area() = 0; // vtable[1] (eskiden [0])
virtual double perimeter() = 0; // vtable[2] (eskiden [1])
virtual ~Shape() = default; // vtable[3] (eskiden [2])
};
// Eski kodla derlenen program: vtable[0]'ı çağırır → area() bekliyor
// Ama yeni vtable'da [0] = name() → yanlış fonksiyon çağrılır!Mevcut sanal fonksiyonların önüne yeni sanal fonksiyon eklemek vtable'ı kaydırır. Sona eklemek ise yalnızca doğrudan bu sınıftan miras alan kodlar için güvenli olabilir — ama genel olarak risklidir.
3. STL Container Layout
// Farklı derleyici versiyonlarında std::string layout değişebilir
// GCC'nin std::string'i copy-on-write'dan small-string-optimization'a geçti
// (GCC 5 = ABI break, libstdc++.so.6'nın dual ABI desteği bu yüzden var)
// Kütüphane arayüzünde STL tipi döndürmek risklidir:
std::vector<std::string> get_names(); // ⚠️ ABI'ye bağlı
// Güvenli alternatif:
size_t get_names(const char** buffer, size_t max_count); // C ABI — stabilTam ABI Kırılma Listesi
| Değişiklik | ABI Kırar mı? |
|---|---|
| Yeni fonksiyon ekle | ❌ Hayır |
| Fonksiyon kaldır | ✅ Evet |
| Struct'a üye ekle | ✅ Evet |
| Sanal fonksiyon ekle/kaldır/sırala | ✅ Evet |
| Non-virtual'ı virtual yap | ✅ Evet |
| Miras hiyerarşisi değiştir | ✅ Evet |
| Enum değeri ekle (sonuna) | ❌ Genellikle hayır |
| Fonksiyon parametresi ekle | ✅ Evet (name mangling) |
| Template instansiasyon değiştir | ✅ Evet |
| Derleyici/STL versiyonu değiştir | ⚠️ Olabilir |
Pimpl Idiom: ABI Stabilite Kalkanı
Pimpl (Pointer to Implementation), sınıfın implementasyon detaylarını bir opaque pointer arkasına saklar. Header'da sadece pointer görünür, implementasyon .cpp dosyasında kalır. Bu, implementasyon değiştiğinde header'ın ve dolayısıyla ABI'nin değişmemesini sağlar.
// ---- database.h (PUBLIC HEADER — kullanıcılar bunu include eder) ----
#pragma once
#include <memory>
#include <string>
#include <cstdint>
class Database {
public:
Database(const std::string& connection_string);
~Database();
// Move semantics
Database(Database&&) noexcept;
Database& operator=(Database&&) noexcept;
// Copy yasak (unique_ptr)
Database(const Database&) = delete;
Database& operator=(const Database&) = delete;
// Public API
bool connect();
void disconnect();
bool execute(const std::string& query);
int64_t last_insert_id() const;
private:
// Implementasyonu TAMAMEN gizle
struct Impl; // Forward declaration
std::unique_ptr<Impl> pimpl_; // Opaque pointer
};
// sizeof(Database) = sizeof(unique_ptr<Impl>) = sizeof(void*) = 8 byte
// Bu boyut ASLA değişmez — ne eklersen ekle Impl'e!// ---- database.cpp (PRIVATE — kütüphane içinde kalır) ----
#include "database.h"
#include <vector>
#include <unordered_map>
#include <mutex>
// İmplementasyon — istediğin kadar değiştir, ABI kırılmaz
struct Database::Impl {
std::string connection_string;
bool connected = false;
int64_t last_id = 0;
// Bu alanları ekle, kaldır, değiştir — ABI etkilenmez!
std::vector<std::string> query_history;
std::unordered_map<std::string, std::string> cache;
std::mutex mutex;
int retry_count = 3; // Yeni eklendi — ABI kırılmadı!
double timeout_seconds = 30.0; // Yeni eklendi — ABI kırılmadı!
bool do_connect() {
// Gerçek bağlantı mantığı...
connected = true;
return true;
}
bool do_execute(const std::string& query) {
std::lock_guard<std::mutex> lock(mutex);
query_history.push_back(query);
// Gerçek sorgu çalıştırma...
last_id = 42;
return true;
}
};
Database::Database(const std::string& connection_string)
: pimpl_(std::make_unique<Impl>())
{
pimpl_->connection_string = connection_string;
}
Database::~Database() = default;
Database::Database(Database&&) noexcept = default;
Database& Database::operator=(Database&&) noexcept = default;
bool Database::connect() { return pimpl_->do_connect(); }
void Database::disconnect() { pimpl_->connected = false; }
bool Database::execute(const std::string& query) { return pimpl_->do_execute(query); }
int64_t Database::last_insert_id() const { return pimpl_->last_id; }Pimpl'in gücü: Impl struct'ına istediğin kadar alan ekle, kaldır, değiştir — header değişmediği sürece ABI kırılmaz. Kullanıcıların yeniden derlemesine gerek kalmaz.
Pimpl'in Maliyeti
Heap allocation: Her nesne için
make_uniqueçağrısı (amaImplgenellikle büyük, maliyet göreceli olarak düşük)İndirection: Her metot çağrısı bir pointer dereference ekler (genellikle önemsiz)
Boilerplate: Forwarding fonksiyonları yazmak gerekir
Performans kritik iç sınıflarda Pimpl gereksiz. Ama public API sınıflarında (kütüphane arayüzü) ABI stabilitesi genellikle bu maliyete değer.
extern "C" ve C-Uyumlu Interface
C++ name mangling yüzünden, farklı derleyicilerle derlenmiş C++ kütüphaneleri birbirleriyle iletişim kuramayabilir. Ama C ABI evrenseldir — her platform aynı C çağrı konvansiyonunu kullanır. Bu yüzden kütüphane arayüzleri sıklıkla C ABI ile expose edilir.
// ---- mylib.h (C ve C++ tarafından kullanılabilir) ----
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
// Opaque handle (C'de class yok, pointer kullanırız)
typedef struct MyEngine_t* MyEngine;
// Yaşam döngüsü
MyEngine myengine_create(const char* config_path);
void myengine_destroy(MyEngine engine);
// İşlemler
int myengine_process(MyEngine engine, const char* input);
const char* myengine_get_result(MyEngine engine);
const char* myengine_get_error(MyEngine engine);
// Versiyon bilgisi
int myengine_version_major(void);
int myengine_version_minor(void);
#ifdef __cplusplus
}
#endif// ---- mylib.cpp (C++ implementasyon) ----
#include "mylib.h"
#include <string>
#include <memory>
// İç implementasyon — tamamen C++
class EngineImpl {
public:
explicit EngineImpl(const std::string& config) : config_(config) {}
int process(const std::string& input) {
try {
result_ = "Processed: " + input;
return 0; // Success
} catch (const std::exception& e) {
error_ = e.what();
return -1; // Error
}
}
const std::string& result() const { return result_; }
const std::string& error() const { return error_; }
private:
std::string config_;
std::string result_;
std::string error_;
};
// C ABI wrapper fonksiyonları
extern "C" {
MyEngine myengine_create(const char* config_path) {
try {
return reinterpret_cast<MyEngine>(new EngineImpl(config_path));
} catch (...) {
return nullptr;
}
}
void myengine_destroy(MyEngine engine) {
delete reinterpret_cast<EngineImpl*>(engine);
}
int myengine_process(MyEngine engine, const char* input) {
if (!engine || !input) return -1;
return reinterpret_cast<EngineImpl*>(engine)->process(input);
}
const char* myengine_get_result(MyEngine engine) {
if (!engine) return "";
return reinterpret_cast<EngineImpl*>(engine)->result().c_str();
}
const char* myengine_get_error(MyEngine engine) {
if (!engine) return "";
return reinterpret_cast<EngineImpl*>(engine)->error().c_str();
}
int myengine_version_major(void) { return 2; }
int myengine_version_minor(void) { return 1; }
}Bu pattern'da C++ implementasyonu tamamen gizli. Dışarıya sadece C fonksiyonları açılıyor. Bu sayede:
Her C++ derleyicisi kütüphaneyi kullanabilir (GCC, Clang, MSVC)
Python, Rust, Go, Java gibi diller FFI ile doğrudan çağırabilir
ABI stabilitesi maksimum — C ABI neredeyse hiç değişmez
⚠️ Dikkat: extern "C" fonksiyonlar exception fırlatamazlar (C'de exception mekanizması yok). C++ exception'ı C koduna ulaşırsa undefined behavior olur. Her extern "C" fonksiyonda try-catch bloğu ile exception'ları hata koduna dönüştürmek şart.
Inline Namespace ile Versioning
C++11'de gelen inline namespace, ABI versiyonlamayı dil düzeyinde destekler. Kullanıcılar namespace'i görmez ama linker farklı versiyonları birbirinden ayırabilir.
// ---- Versiyon 1: mylib v1 ----
namespace mylib {
inline namespace v1 {
struct Config {
int width;
int height;
};
void init(const Config& cfg);
}
}
// Kullanıcı kodu:
// mylib::Config cfg{800, 600}; // Aslında mylib::v1::Config
// mylib::init(cfg); // Aslında mylib::v1::init
// Mangled: _ZN5mylib2v14initERKNS0_6ConfigE
// ---- Versiyon 2: mylib v2 ----
namespace mylib {
namespace v1 { // Artık inline DEĞİL
struct Config {
int width;
int height;
};
void init(const Config& cfg);
}
inline namespace v2 { // Yeni versiyon inline
struct Config {
int width;
int height;
bool fullscreen; // Yeni alan — ABI farklı
int refresh_rate; // Yeni alan
};
void init(const Config& cfg);
}
}
// Yeni kullanıcı kodu:
// mylib::Config cfg{800, 600, true, 144}; // mylib::v2::Config
// mylib::init(cfg); // mylib::v2::init
// Mangled: _ZN5mylib2v24initERKNS0_6ConfigE — v1'den FARKLI!
// Eski kodu açıkça v1 ile kullanmak isteyen:
// mylib::v1::Config cfg{800, 600};
// mylib::v1::init(cfg);Linker düzeyinde v1::init ve v2::init farklı sembollerdir. Bu, aynı binary'de iki versiyonun birlikte var olmasını sağlar. libstdc++ bunu string implementasyonu değiştiğinde (GCC 5 ABI break) kullanmıştır.
Gerçek Dünya: libstdc++ Dual ABI
// GCC 5+ libstdc++ dual ABI
namespace std {
// Eski ABI (COW string)
namespace __cxx03 {
class basic_string { /* ... */ };
}
// Yeni ABI (SSO string) — varsayılan
inline namespace __cxx11 {
class basic_string { /* ... */ };
}
}
// _GLIBCXX_USE_CXX11_ABI=0 → eski ABI
// _GLIBCXX_USE_CXX11_ABI=1 → yeni ABI (varsayılan)Bu sayede GCC 5 öncesi derlenmiş kütüphaneler hâlâ çalışır — __cxx03 namespace'indeki string'i kullanmaya devam ederler.
Kütüphane Versiyonlama
SONAME (Linux Shared Library Versioning)
Linux'ta shared library'ler SONAME (Shared Object Name) ile versiyonlanır:
# Kütüphane dosya isimlendirme konvansiyonu
libmylib.so # Sembolik link → development
libmylib.so.2 # Sembolik link → SONAME (major versiyon)
libmylib.so.2.1 # Sembolik link → tam versiyon
libmylib.so.2.1.3 # Gerçek dosya
# SONAME ayarlama (CMake)
# CMakeLists.txt
set_target_properties(mylib PROPERTIES
VERSION 2.1.3 # Tam versiyon
SOVERSION 2 # Major versiyon (ABI versiyonu)
)# CMake ile tam versiyonlama örneği
cmake_minimum_required(VERSION 3.16)
project(mylib VERSION 2.1.3 LANGUAGES CXX)
add_library(mylib SHARED
src/mylib.cpp
)
target_include_directories(mylib PUBLIC
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
set_target_properties(mylib PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
CXX_VISIBILITY_PRESET hidden # Sadece export edilen semboller görünür
VISIBILITY_INLINES_HIDDEN ON
)
# Export makrosu
include(GenerateExportHeader)
generate_export_header(mylib
EXPORT_FILE_NAME ${CMAKE_BINARY_DIR}/include/mylib_export.h
)Semantic Versioning (SemVer)
MAJOR.MINOR.PATCH
MAJOR: ABI kırılan değişiklik (struct layout, fonksiyon kaldırma)
MINOR: Geriye uyumlu yeni özellik (yeni fonksiyon ekleme)
PATCH: Bug fix (davranış değişikliği yok)| Değişiklik | Versiyon Artışı |
|---|---|
| Bug fix | 2.1.3 → 2.1.4 |
| Yeni fonksiyon ekleme | 2.1.3 → 2.2.0 |
| Struct layout değişikliği | 2.1.3 → 3.0.0 |
| Sanal fonksiyon ekleme | 2.1.3 → 3.0.0 |
| Fonksiyon kaldırma | 2.1.3 → 3.0.0 |
Symbol Visibility
Tüm sembolleri export etmek yerine, sadece public API'yi export et. Bu hem ABI yüzeyini küçültür hem de link performansını artırır.
// Export makrosu — platform bağımsız
#if defined(_WIN32)
#ifdef MYLIB_BUILDING
#define MYLIB_API __declspec(dllexport)
#else
#define MYLIB_API __declspec(dllimport)
#endif
#elif defined(__GNUC__) || defined(__clang__)
#define MYLIB_API __attribute__((visibility("default")))
#else
#define MYLIB_API
#endif
// Sadece export edilen fonksiyonlar dışarıdan erişilebilir
class MYLIB_API PublicClass {
public:
void public_method();
private:
void internal_method(); // Export edilir ama private (ABI'nin parçası)
};
// Export EDİLMEYEN — kütüphane dışından erişilemez
class InternalHelper {
// Bu sınıf dilediğin gibi değiştirilebilir — ABI etkilenmez
};💡 İpucu: CMake'in generate_export_header komutu, platform bağımsız export makrolarını otomatik oluşturur. Elle #ifdef _WIN32 yazmak yerine bunu kullan.
ABI Checker Araçları
abi-compliance-checker
İki kütüphane versiyonu arasındaki ABI farklarını otomatik tespit eder.
# Kurulum (Ubuntu/Debian)
sudo apt install abi-compliance-checker abi-dumper
# Adım 1: Eski versiyonun ABI dump'ını al
abi-dumper libmylib.so.1.0.0 -o v1.dump -lver 1.0
# Adım 2: Yeni versiyonun ABI dump'ını al
abi-dumper libmylib.so.2.0.0 -o v2.dump -lver 2.0
# Adım 3: Karşılaştır
abi-compliance-checker -l mylib -old v1.dump -new v2.dump
# Çıktı: HTML rapor — kırılan, eklenen, değişen sembollerRapor şunları gösterir:
Kaldırılan semboller (fonksiyon/sınıf)
Değişen struct/class layout (boyut, üye offset)
Değişen fonksiyon imzaları
Vtable değişiklikleri
Uyumluluk yüzdesi (örn: %87 binary uyumluluk)
Diğer Araçlar
# libabigail — Red Hat'in ABI analiz aracı
abidiff libmylib.so.old libmylib.so.new
# nm — sembol listesi
nm -DC libmylib.so | head -20 # -D: dynamic, -C: demangle
# readelf — ELF binary analizi
readelf -d libmylib.so | grep SONAME # SONAME kontrol
readelf -s libmylib.so # Sembol tablosu
# objdump — detaylı analiz
objdump -T libmylib.so # Dynamic sembol tablosuGerçek Dünya Örneği: ABI-Stabil Kütüphane
Tüm kavramları birleştiren bir örnek: Pimpl, extern "C", symbol visibility, versioning.
// ---- include/imagelib.h (Public API) ----
#pragma once
#include <cstdint>
#include <cstddef>
// Platform-bağımsız export makrosu
#if defined(IMAGELIB_SHARED)
#if defined(_WIN32)
#ifdef IMAGELIB_BUILDING
#define IMAGELIB_API __declspec(dllexport)
#else
#define IMAGELIB_API __declspec(dllimport)
#endif
#else
#define IMAGELIB_API __attribute__((visibility("default")))
#endif
#else
#define IMAGELIB_API
#endif
// C++ API (Pimpl ile ABI-stabil)
#ifdef __cplusplus
#include <memory>
#include <string>
namespace imagelib {
inline namespace v2 { // ABI versiyonu
class IMAGELIB_API Image {
public:
Image();
~Image();
Image(Image&&) noexcept;
Image& operator=(Image&&) noexcept;
bool load(const std::string& path);
bool save(const std::string& path) const;
int32_t width() const;
int32_t height() const;
int32_t channels() const;
const uint8_t* data() const;
bool resize(int32_t new_width, int32_t new_height);
bool to_grayscale();
private:
struct Impl;
std::unique_ptr<Impl> pimpl_;
};
} // namespace v2
} // namespace imagelib
#endif // __cplusplus
// C API (maksimum uyumluluk)
#ifdef __cplusplus
extern "C" {
#endif
typedef struct ImageHandle_t* ImageHandle;
IMAGELIB_API ImageHandle imagelib_create(void);
IMAGELIB_API void imagelib_destroy(ImageHandle img);
IMAGELIB_API int imagelib_load(ImageHandle img, const char* path);
IMAGELIB_API int imagelib_save(ImageHandle img, const char* path);
IMAGELIB_API int32_t imagelib_width(ImageHandle img);
IMAGELIB_API int32_t imagelib_height(ImageHandle img);
IMAGELIB_API int imagelib_resize(ImageHandle img, int32_t w, int32_t h);
IMAGELIB_API int imagelib_to_grayscale(ImageHandle img);
// Versiyon bilgisi
IMAGELIB_API int imagelib_version_major(void);
IMAGELIB_API int imagelib_version_minor(void);
IMAGELIB_API int imagelib_version_patch(void);
IMAGELIB_API const char* imagelib_version_string(void);
#ifdef __cplusplus
}
#endifBu header, hem C++ hem C kullanıcılarına hitap eder. C++ API Pimpl ile korunur, C API opaque handle ile korunur. inline namespace v2 ile ABI versiyonu linker düzeyinde takip edilir. Symbol visibility ile sadece IMAGELIB_API işaretli semboller export edilir.
ABI uyumluluğu garantisi vermek ciddi bir taahhüttür. Her yeni versiyon çıkışında ABI checker çalıştır, CI pipeline'ına entegre et ve major versiyon artışlarını açıkça belgele. Bir kez ABI promise verdiğinde, geri dönmek zordur.
Özet
ABI (Application Binary Interface), derlenmiş kodun bellek düzeni, çağrı konvansiyonu ve sembol isimlerini tanımlar — API uyumlu ama ABI uyumsuz kod sessizce crash eder.
Name mangling, C++ overloaded fonksiyonları linker düzeyinde benzersiz kılar — ama derleyiciler arası uyumsuzluğun temel nedenidir.
Struct üyesi ekleme, sanal fonksiyon sırası değiştirme, miras değişikliği ABI kıran en yaygın değişikliklerdir — her biri sessiz data corruption'a yol açabilir.
Pimpl idiom, implementasyonu opaque pointer arkasına saklayarak header'ı (ve dolayısıyla ABI'yi) stabilize eder — public kütüphane sınıfları için standart yaklaşımdır.
extern "C", C++ name mangling'i devre dışı bırakarak evrensel C ABI ile dışa açılım sağlar — cross-language FFI için zorunlu, exception'ları try-catch ile yakalamak şart.
Inline namespace ve SONAME/SemVer ile kütüphane versiyonlama, eski ve yeni kodun güvenle birlikte çalışmasını sağlar —
abi-compliance-checkergibi araçlarla CI'da otomatik kontrol et.
AI Asistan
Sorularını yanıtlamaya hazır