How to declare, initialize, and print a complex data structure in C++

1 Answer

0 votes
#include <iostream>
#include <optional>
#include <string>
#include <array>
#include <vector>
#include <chrono>
#include <variant>
#include <map>
#include <functional>
#include <cstring>

// Simple custom allocator that counts allocations
template <typename T>
struct CountingAllocator : std::allocator<T> {
    using Base = std::allocator<T>;
    using value_type = T;

    int* counter;

    CountingAllocator(int* c = nullptr) noexcept : counter(c) {}

    template <class U>
    CountingAllocator(const CountingAllocator<U>& other) noexcept
        : counter(other.counter) {}

    T* allocate(std::size_t n) {
        if (counter) {
            *counter += static_cast<int>(n);
        }
        return Base::allocate(n);
    }

    void deallocate(T* p, std::size_t n) noexcept {
        Base::deallocate(p, n);
    }
};

// A simple nested struct representing an address
struct Address {
    std::string street;
    std::string city;
    int zip;
};

// A more complex struct containing many different C++ features
struct Person {
    Address homeAddress;                         // Nested struct
    std::array<int, 5> favoriteNumbers;          // Fixed-size array
    char* nickname;                              // Raw pointer
    std::chrono::year_month_day birthDate;       // C++20 date type
    std::unique_ptr<std::string> notes;          // Smart pointer
    std::optional<std::string> middleName;       // Optional field
    std::vector<std::string> hobbies;            // Dynamic container

    // Scoped enum inside the struct
    enum class Status { Active, Inactive, Unknown };
    Status status;

    // Type-safe union
    std::variant<int, std::string, std::vector<int>> id;

    // Associative container
    std::map<std::string, std::string> metadata;

    // Bitfields for compact flags
    struct Flags {
        unsigned isAdmin   : 1;
        unsigned isGuest   : 1;
        unsigned isVerified: 1;
    } flags;

    // Static members
    static int population;
    static int allocatedInts;

    // Custom allocator usage
    std::vector<int, CountingAllocator<int>> scores;

    // Function pointer / std::function
    using PrintFn = std::function<void(const Person&)>;
    static PrintFn extraPrinter;

    // Templates inside the struct
    template <typename T>
    void setMeta(const std::string& key, const T& value) {
        metadata[key] = std::to_string(value);
    }

    void setMeta(const std::string& key, const char* value) {
        metadata[key] = value;
    }

    template <typename T>
    static T identity(const T& v) {
        return v;
    }

    // Constructor initializes some fields with defaults
    Person()
        : nickname(nullptr),
          birthDate(std::chrono::year{1990}/std::chrono::month{1}/std::chrono::day{1}),
          notes(std::make_unique<std::string>("No notes")),
          status(Status::Unknown),
          flags{0, 0, 0},
          scores(CountingAllocator<int>(&allocatedInts))
    {
        ++population;
    }

    ~Person() {
        --population;
    }
};

int Person::population = 0;
int Person::allocatedInts = 0;
Person::PrintFn Person::extraPrinter = nullptr;

// Prints all fields of the Person struct
void printPerson(const Person& p) {
    std::cout << "Address: " << p.homeAddress.street << ", "
              << p.homeAddress.city << " " << p.homeAddress.zip << "\n";

    std::cout << "Favorite Numbers: ";
    for (int n : p.favoriteNumbers) std::cout << n << " ";
    std::cout << "\n";

    std::cout << "Nickname: "
              << (p.nickname ? p.nickname : "(none)") << "\n";

    std::cout << "Birth Date: "
              << std::format("{}-{:02}-{:02}",
                    int(p.birthDate.year()),
                    unsigned(p.birthDate.month()),
                    unsigned(p.birthDate.day()))
              << "\n";

    std::cout << "Notes: " << *p.notes << "\n";

    std::cout << "Middle Name: "
              << (p.middleName ? *p.middleName : "(none)") << "\n";

    std::cout << "Hobbies: ";
    for (const auto& h : p.hobbies) std::cout << h << ", ";
    std::cout << "\n";

    std::cout << "Status: ";
    switch (p.status) {
        case Person::Status::Active:   std::cout << "Active";   break;
        case Person::Status::Inactive: std::cout << "Inactive"; break;
        default:                       std::cout << "Unknown";  break;
    }
    std::cout << "\n";

    std::cout << "ID (variant): ";
    std::visit([](const auto& v) {
        using V = std::decay_t<decltype(v)>;
        if constexpr (std::is_same_v<V, int>) {
            std::cout << "int: " << v;
        } else if constexpr (std::is_same_v<V, std::string>) {
            std::cout << "string: " << v;
        } else if constexpr (std::is_same_v<V, std::vector<int>>) {
            std::cout << "vector<int>: ";
            for (int x : v) std::cout << x << " ";
        }
    }, p.id);
    std::cout << "\n";

    std::cout << "Metadata:\n";
    for (const auto& [k, v] : p.metadata) {
        std::cout << "  " << k << " = " << v << "\n";
    }

    std::cout << "Flags: "
              << "isAdmin=" << p.flags.isAdmin
              << " isGuest=" << p.flags.isGuest
              << " isVerified=" << p.flags.isVerified
              << "\n";

    std::cout << "Scores (custom allocator): ";
    for (int s : p.scores) std::cout << s << " ";
    std::cout << "\n";

    std::cout << "Population (static): " << Person::population << "\n";
    std::cout << "Allocated ints (custom allocator): " << Person::allocatedInts << "\n";

    if (Person::extraPrinter) {
        std::cout << "Extra printer:\n";
        Person::extraPrinter(p);
    }
}

int main() {
    Person p;  // Create a Person instance

    p.homeAddress = {"Baxter", "New York", 426900};

    p.favoriteNumbers = {7, 18, 27, 42, 81};

    p.nickname = new char[10];
    std::snprintf(p.nickname, 10, "Orion");

    p.birthDate = std::chrono::year{1026}/std::chrono::month{6}/std::chrono::day{15};

    *p.notes = "Neo likes C/C#/Python and structs";

    p.middleName = "ECHO-9";

    p.hobbies = {"Coding", "Dreaming", "Walking", "Sci-Fi Movies"};

    p.status = Person::Status::Active;

    p.id = 42;
    p.setMeta("role", "Explorer");
    p.setMeta("level", 9);
    p.flags.isAdmin = 1;
    p.flags.isVerified = 1;

    p.scores.push_back(100);
    p.scores.push_back(95);

    Person::extraPrinter = [](const Person& person) {
        std::cout << "  [extra] nickname length: "
                  << (person.nickname ? ::strlen(person.nickname) : 0)
                  << "\n";
    };


    printPerson(p);

    delete[] p.nickname;
}



/*
run:

Address: Baxter, New York 426900
Favorite Numbers: 7 18 27 42 81 
Nickname: Orion
Birth Date: 1026-06-15
Notes: Neo likes C/C#/Python and structs
Middle Name: ECHO-9
Hobbies: Coding, Dreaming, Walking, Sci-Fi Movies, 
Status: Active
ID (variant): int: 42
Metadata:
  level = 9
  role = Explorer
Flags: isAdmin=1 isGuest=0 isVerified=1
Scores (custom allocator): 100 95 
Population (static): 1
Allocated ints (custom allocator): 3
Extra printer:
  [extra] nickname length: 5

*/

 



answered Sep 3 by avibootz

Related questions

...