![]() |
Name Generator 4.2.1
Culture-aware name generation for C++23
|
This guide covers every feature of the name-generator library in detail. For a quick overview, see the README. For the full API reference, run doxygen Doxyfile from the repository root and open doc/api/html/index.html.
dasmig/namegen.hpp, dasmig/core.hpp and the dasmig/core/ directory into your include path, keeping them side by side.resources/ folder (containing .names files) to your working directory. Choose resources/lite/ (3 MB) for compact datasets or resources/full/ (41 MB) for complete name lists. Or compile lite in (see Compiled-in Data).-std=c++23 (GCC/Clang) or /std:c++latest (MSVC).The singleton ng::instance() loads the lite tier on first access, or full when lite is not found. It uses the compiled-in copy when built with DASMIG_NAME_EMBED, and otherwise looks in resources, ../resources, name-generator/resources and ../name-generator/resources, relative to the working directory.
Two dataset tiers are available:
| Tier | Size | Content |
|---|---|---|
dataset::lite | 3 MB | Up to 500 names per list (133,009 names) |
dataset::full | 41 MB | Complete name lists (2,273,752 names) |
Where the names come from, how they are weighted and what they leave out is in Data and Method. The full tier takes about 2 s to load (optimised build), the lite tier about 0.15 s.
Load a specific tier:
load(dataset) probes the same base paths as the singleton and returns true if the tier was found. Loading replaces the names and any layers loaded before.
For custom locations:
load(path) reads every .names file below the directory, in path order, and replaces what was loaded. It does nothing if the path does not exist or is not a directory, so use has_resources() to verify that databases were actually loaded. A malformed file throws dasmig::core::tsv_error with its file, line and column, and leaves the names unchanged.
Check whether resources loaded successfully:
105 cultures are supported:
| Culture | Code | Culture | Code | Culture | Code |
|---|---|---|---|---|---|
| Afghan | af | Filipino | ph | Moldovan | md |
| Albanian | al | Finnish | fi | Moroccan | ma |
| Algerian | dz | French | fr | Namibian | na |
| American | us | Georgian | ge | Nigerian | ng |
| Angolan | ao | German | de | Norwegian | no |
| Argentinian | ar | Ghanaian | gh | Omani | om |
| Austrian | at | Greek | gr | Palestinian | ps |
| Azerbaijani | az | Guatemalan | gt | Panamanian | pa |
| Bahraini | bh | Haitian | ht | Peruvian | pe |
| Bangladeshi | bd | Honduran | hn | Polish | pl |
| Belgian | be | Hong Konger | hk | Portuguese | pt |
| Bolivian | bo | Hungarian | hu | Puerto Rican | pr |
| Botswanan | bw | Icelandic | is | Qatari | qa |
| Brazilian | br | Indian | in | Russian | ru |
| British | gb | Indonesian | id | Salvadoran | sv |
| Bruneian | bn | Iranian | ir | Saudi | sa |
| Bulgarian | bg | Iraqi | iq | Serbian | rs |
| Burkinabe | bf | Irish | ie | Singaporean | sg |
| Burundian | bi | Israeli | il | Slovenian | si |
| Cambodian | kh | Italian | it | South African | za |
| Cameroonian | cm | Jamaican | jm | Spanish | es |
| Canadian | ca | Japanese | jp | Sudanese | sd |
| Chilean | cl | Jordanian | jo | Swedish | se |
| Chinese | cn | Kazakh | kz | Swiss | ch |
| Colombian | co | Korean | kr | Syrian | sy |
| Costa Rican | cr | Kuwaiti | kw | Taiwanese | tw |
| Croatian | hr | Lebanese | lb | Tunisian | tn |
| Cypriot | cy | Libyan | ly | Turkish | tr |
| Czech | cz | Lithuanian | lt | Turkmen | tm |
| Danish | dk | Luxembourgish | lu | Uruguayan | uy |
| Djiboutian | dj | Macanese | mo | Yemeni | ye |
| Dutch | nl | Malaysian | my | ||
| Ecuadorian | ec | Maldivian | mv | ||
| Egyptian | eg | Maltese | mt | ||
| Emirati | ae | Mauritian | mu | ||
| Estonian | ee | Mexican | mx | ||
| Ethiopian | et | ||||
| Fijian | fj |
With culture::any a culture is chosen uniformly among the loaded ones, not weighted by population; to follow a population, choose the country first (for example with country-generator) and pass its culture.
The name return type supports chained appending:
Each appended part draws from the name's seed and its position, so replaying the seed and the same appends gives the same full name (see Replay and JSON).
Each ng instance is independent. The static instance() singleton uses a local static for safe initialization.
| Operation | Thread-safe? |
|---|---|
instance() | Yes (static local) |
get_name(request) / get_surname(request) on different instances | Yes |
get_name(request) / get_surname(request) on the same instance | No — requires external synchronization |
load(), extend() | No — must not be called concurrently with generation on the same instance |
For concurrent generation, give each thread its own ng instance. The seeded overloads (get_name(request, seed), get_surname(request, seed)) are const and do not touch the internal engine.
A name object holds a back-pointer to the ng instance that created it. Calling append_name() or append_surname() on a name whose ng instance has been destroyed or moved from is undefined behavior. Keep the generator alive for as long as you need to chain appends:
Pass an explicit seed to produce the same name every time:
Every name records its seed. Retrieve it with name::seed():
There is no generator-level seed. For a reproducible sequence, derive each call's seed yourself:
A call without a seed draws a fresh non-deterministic one, which name::seed() reports.
Construct independent ng objects with their own name databases:
Instances with the same data give the same name for a seed:
Instances can be moved (but not copied):
The shipped lists are a base layer. Files in the same .names format are loaded on top of them to add names, change their weights or remove them, without editing the shipped files. Each file says which list it changes on its first two lines (country code, then male, female or any for surnames, or male-surname or female-surname for the cultures with gendered surnames, see data.md); within a list, names are the keys.
1 / sqrt(rank) (the top name about 0.7 to 1.0, rank 500 about 0.045), and a name without a weight counts 1, so it is about as likely as the most popular name.dasmig::core::tsv_error with the file, line and column, and the names stay as they were; a directory is applied all or nothing.layers(culture, gender) lists what is loaded for one list (gender::any for surnames): source, mode, names added, replaced and removed, and warnings.load(...) replaces the base and drops every layer.Every result records the seed and the data it came from:
to_json() gives the result as JSON (UTF-8), the form the WebAssembly layer and the game consume:
gender is the gender of the forenames (for a surname, the one append_name() would use) and culture the culture of the first part. Seeds are strings because JavaScript numbers cannot hold every 64-bit value. to_json(writer) writes into an existing dasmig::core::json_writer, for composing larger documents.
Define DASMIG_NAME_EMBED before including the header to compile the lite tier into the program (a 4 MB header; about 5 s to compile with clang). load(dataset::lite) and instance() then use it and need no files, which is what a WebAssembly build wants. The compiled-in data has the same data_hash() as the files, so seeds replay the same either way. The full tier is always read from disk.
dasmig/namegen_data.hpp is generated from resources/lite by make embed; CI checks it is up to date.
Version 3 moves the library onto dasmig-core.
std::discrete_distribution gave different names with libstdc++, libc++ and MSVC. Seeds saved with v2 do not reproduce their v2 results. The distributions are unchanged (checked against v2 over 20,000 draws per scenario).load(path) replaces** the names instead of adding to them; use extend() to add. A malformed file now throws dasmig::core::tsv_error (v2 skipped files with an unknown country code, read any second line other than male/female as surnames, and dropped invalid UTF-8 bytes). load(dataset) is [[nodiscard]].resources/full lost two folders v2 never loaded (australian, ukranian). The lists are otherwise unchanged.random.hpp with core.hpp and the core/ directory (see Installation).extend(), extend_from_string(), layers(), data_hash(), culture_code(), culture_name(), name::signature(), name::to_json(), DASMIG_NAME_EMBED, a disabled column.Version 4 makes a request the only way to call a getter.
get_name(gender, culture[, seed]) and get_surname(culture[, seed]) become get_name(name_request[, seed]) and get_surname(name_request[, seed]), e.g. get_name({.gender = gender::f}); use get_name({}) for the defaults. ng::seed() and ng::unseed() are removed; pass a seed per call.male.sur.names, female.sur.names), so a surname for those cultures follows the requested gender. Seeded results for those cultures differ from v3.Every seeded getter has batch forms: get_name_batch, get_surname_batch return a std::vector, and for_each_name streams the items to a sink in index order without holding the batch.
i is exactly get_name(request, core::item_seed(batch_seed, first + i)). A range can be generated directly (first = 1000 starts at item 1000), two batches that follow each other equal one, and the output is the same for any threads (0 means the hardware count). Without a batch_seed one is drawn like the single getter draws its own.core::batch_options has count, first, threads and unique. The request describes one item; the options describe the batch..unique = core::unique_rule::by<name>(key) regenerates an item whose key already appeared in the batch, with a deterministic retry seed, and throws std::runtime_error naming the item when the attempts run out.core::jsonl_writer writes one to_json() per line, core::csv_writer flattens each result's JSON into dotted columns. Any callable (std::uint64_t index, name&) works.| Exception | Thrown by | Condition |
|---|---|---|
std::invalid_argument | get_name(request) / get_surname(request) / append_*() | No names loaded for the resolved culture/gender |
std::invalid_argument | extend(path) | A file without the .names extension |
std::out_of_range | layers(culture, gender) | That list was never loaded |
dasmig::core::tsv_error | load(), extend(), extend_from_string() | A malformed file or layer (message gives file, line and column); the names are unchanged |
Note:
load(path)does not throw if the path is missing — it silently returns. Usehas_resources()to check.