AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / user-1386054

Adrian McCarthy's questions

Martin Hope
Adrian McCarthy
Asked: 2025-04-20 09:02:47 +0800 CST

O vinculador remove globais (e seus construtores) da biblioteca estática

  • 7

Tenho um registro singleton que mapeia nomes para ponteiros de função. Também tenho um objeto registrador cujo construtor registra um ponteiro de função.

Meu objetivo é ter as funções e o registro em uma biblioteca estática, mas descobri que o vinculador omite os registros quando construído dessa forma.

Aqui está uma ilustração simplificada. Para resumir, substituí os ponteiros de função por ponteiros para inteiros globais e o contêiner associativo do registro por um vetor.

// registry.h
#include <vector>
std::vector<int const *> &GetRegistry();
// registry.cpp
#include "registry.h"

std::vector<int const *> &GetRegistry() {
    static std::vector<int const *> registry;
    return registry;
}
// thingadder.h
#include "registry.h"

class ThingAdder {
    public:
        explicit ThingAdder(int const *thing) {
            GetRegistry().push_back(thing);
        }
};
// things.cpp
#include "thingadder.h"

int g_thing1 = 1;
ThingAdder g_adder1(&g_thing1);
// main.cpp
#include "registry.h"
#include <print>
#include <vector>

int main() {
    std::print("registry size: {}\n", GetRegistry().size());
    return 0;
}

Se todos os arquivos forem compilados e vinculados como um único projeto, g_adder1o construtor de adiciona g_global1o endereço de ao registro, e o programa informa que o tamanho do registro é 1.

Mas quando tudo, exceto main.cpp, é criado em uma biblioteca estática, e então main.cpp é compilado e vinculado a essa biblioteca, o tamanho do registro relatado é 0. Parece que os globais em things.cpp foram omitidos pelo link.

Eu meio que entendo por que isso está acontecendo: nada fora de things.cpp faz referência direta ao objeto global nem ao seu registrador. Mas isso é verdade mesmo quando construído como um monolito. Eu não esperaria que incluir essa parte em uma biblioteca estática mudasse o comportamento.

Soluções?

A única solução que encontrei foi fazer com que main.cpp faça referência a um símbolo definido na unidade de tradução things.cpp. Na biblioteca em si, haverá mais arquivos de objetos a serem registrados, e não quero que cada usuário da biblioteca adicione uma referência a um símbolo para cada um deles.

c++
  • 1 respostas
  • 86 Views
Martin Hope
Adrian McCarthy
Asked: 2025-04-10 03:37:51 +0800 CST

std::format manipula o tipo definido pelo usuário se for iterável‽

  • 6

Atualizei alguns códigos mais antigos para usar std::formate fiquei surpreso ao descobrir que funcionava, apesar de eu ter esquecido de fornecer uma std::formatterespecialização para esse tipo.

Imediatamente fiz um pequeno programa de teste para tentar reproduzir isso, mas eles sempre apresentavam um erro de compilação, como eu esperava.

Depois de horas de depuração, descobri que, se o tipo personalizado tiver métodos públicos begine end, a biblioteca formatará a sequência como uma lista separada por vírgulas entre colchetes.

P: Isso é um recurso definido por padrões std::formatou um bug de implementação? (Ou outra coisa?)

Aqui está uma reprodução independente:

#include <array>
#include <print>

class MyType {
    public:
        MyType() : m_values{1, 2, 3, 4} {}

        using internal_type = std::array<int, 4>;
        using const_iterator = typename internal_type::const_iterator;

        const_iterator cbegin() const { return m_values.cbegin(); }
        const_iterator cend()   const { return m_values.cend();   }
        const_iterator begin()  const { return cbegin(); }
        const_iterator end()    const { return cend();   }

    private:
        internal_type m_values;
};

int main() {
    MyType foo;
    // Since MyType is a user-defined type, I would not
    // expect this print statement to compile without a
    // specialization of std::formatter, but because
    // it's iterable, it prints: "foo = [1, 2, 3, 4]\n".
    std::print("foo = {}\n", foo);
    return 0;
}

Estou usando o MS VC++ do Visual Studio 17.12.15 e compilando com /std:c++latest.

c++
  • 1 respostas
  • 70 Views
Martin Hope
Adrian McCarthy
Asked: 2024-10-21 01:26:58 +0800 CST

Devo `std::move` do `std::future`?

  • 5

Digamos que eu tenha uma função que retorna um std::vectore eu queira chamá-la de forma assíncrona.

std::vector<int> foo(int n);

int main() {
    auto future_vector = std::async(foo, 999);
    // ... other stuff

    // Questions are all about this:
    auto actual_vector = future_vector.get();

    // ... do stuff with actual_vector ...
}

Q1: actual_vectorO copy-constructed ou o move-constructed são do estado compartilhado? Preciso escrever std::move(future_vector.get())explicitamente?

Q2: Supondo que a função seja concluída e não cumpra mais a promessa: o tempo de vida do estado compartilhado termina quando geté chamado no futuro ou ele persiste até que o futuro atinja o fim de seu tempo de vida?

c++
  • 1 respostas
  • 85 Views
Martin Hope
Adrian McCarthy
Asked: 2024-09-19 01:04:53 +0800 CST

Projetando API de função para evitar problemas de tempo de vida do objeto

  • 6

Tenho uma função que retorna um lambda.

auto make_scanner(std::vector<int> const &v) {
    auto begin = v.cbegin();
    auto end = v.cend();
    return [begin, end] () mutable -> int {
        return (begin != end) ? *begin++ : 0;
    };
}

Note que o lambda captura iteradores no vetor, mas não o vetor em si. No caso de uso comum, isso é bom:

// Common Use Case
std::vector<int> data = {1, 2, 3};
auto scanner = make_scanner(data);
for (int x = scanner(); x != 0; x = scanner()) {
    // compute something
}

Mas se o chamador fizer um scanner a partir de um vetor temporário sem nome, o lambda ficará com iteradores pendentes:

// Incorrect Use Case
auto scanner = make_scanner(std::vector<int>{1, 2, 3});
// Invoking scanner() is UB because the iterators reference
// the vector after its lifetime.

Em uma compilação não otimizada (ou seja, uma compilação de depuração), isso geralmente funcionará como pretendido, mesmo que o código esteja errado. Em uma compilação otimizada, se você tiver sorte, isso travará ou pelo menos fará com que os testes falhem.

Existe uma maneira de falhar a compilação quando make_scanner é chamado com um vetor temporário sem nome e ainda permitir o caso de uso comum?

Tenho tentado várias sobrecargas com referências de valor r, mas a resposta não me vem à mente.

(Sim, eu percebo que é possível usar um vetor não temporário e ainda acabar com iteradores pendurados no lambda. Não estou preocupado com esses casos neste momento.)

c++
  • 2 respostas
  • 50 Views
Martin Hope
Adrian McCarthy
Asked: 2024-08-30 03:17:38 +0800 CST

Selecionando um tipo de distribuição uniforme da biblioteca padrão

  • 6

tl;dr: Dado um tipo numérico T, existe uma maneira concisa de declarar uma variável como std::uniform_int_distribution<T>ou std::uniform_real_distribution<T>dependendo se T é integral ou de ponto flutuante?


Eu precisava produzir std::chrono::durations aleatórios uniformemente distribuídos em um intervalo definido pelo chamador, então criei um uniform_duration_distributionmodelo de classe modelado com base nos modelos de classe de distribuição da biblioteca padrão.

Primeiro, escrevi um conceito para restringir minha distribuição a uma duração cronológica (ou tipo similar).

// Is T similar enough to std::chrono::duration for our purposes?
template <typename T>
concept chrono_duration = requires (T d) {
    { d.count() } -> std::same_as<typename T::rep>;
    { d.zero()  } -> std::same_as<T>;
    { d.max()   } -> std::same_as<T>;
};

Uma duração tem uma representação numérica chamada contagem. Minha classe contém uma distribuição uniforme numérica da biblioteca padrão, usa-a para gerar uma contagem e constrói uma duração a partir dessa contagem.

template<chrono_duration DurationT>
class uniform_duration_distribution {
  // ...
  private:
    using rep = typename DurationT::rep;
    std::uniform_distribution<rep> m_distribution;  // Whoops!
};

E aí está o problema. O tipo da contagem da duração pode ser um tipo integral ou um tipo de ponto flutuante, então o tipo de m_distributionnão é tão simples std::uniform_distribution<T>porque não existe tal modelo.

Eu não queria fazer várias especializações da minha classe, e não queria limitar os chamadores a uma instanciação específica de uma duração. Eu só queria escolher o tipo para a distribuição contida com base no tipo de rep da duração.

Minha primeira tentativa foi usar um modelo de alias de tipo restrito por conceitos.

template <std::integral IntT>
using dist_selector = std::uniform_int_distribution<IntT>;

template <std::floating_point FloatT>
using dist_selector = std::uniform_real_distribution<FloatT>;

Isso não parece ser permitido. Eu posso (aparentemente) restringir um único modelo de alias using com um conceito, mas não posso usar conceitos para selecionar entre diferentes aliases. Pelo menos, não como eu tentei. Existe uma maneira de fazer isso?

Também aprendi que não posso me especializar usando modelos de alias.

No final, criei um modelo de estrutura com especializações para os tipos numéricos.

// Select the appropriate distribution type based on the value type.
template <typename T> struct dist_selector {};
template <> struct dist_selector<long double>        { using t = std::uniform_real_distribution<long double>; };
template <> struct dist_selector<double>             { using t = std::uniform_real_distribution<double>; };
template <> struct dist_selector<float>              { using t = std::uniform_real_distribution<float>; };
template <> struct dist_selector<long long>          { using t = std::uniform_int_distribution<long long>; };
template <> struct dist_selector<long>               { using t = std::uniform_int_distribution<long>; };
template <> struct dist_selector<int>                { using t = std::uniform_int_distribution<int>; };
template <> struct dist_selector<short>              { using t = std::uniform_int_distribution<short>; };
template <> struct dist_selector<unsigned long long> { using t = std::uniform_int_distribution<unsigned long long>; };
template <> struct dist_selector<unsigned long>      { using t = std::uniform_int_distribution<unsigned long>; };
// ...

Então a variável membro é definida como:

using rep = typename DurationT::rep;
using dist_type = typename dist_selector<rep>::t;
dist_type m_distribution;

Isso funciona, mas parece que estou voltando para um hack antigo. Estou esquecendo de uma maneira mais moderna de fazer isso?

c++
  • 2 respostas
  • 64 Views
Martin Hope
Adrian McCarthy
Asked: 2024-07-07 00:52:34 +0800 CST

Um back_insert_iterator é válido durante toda a vida útil do contêiner?

  • 9

Acho que sei a resposta para isso, mas apreciaria uma verificação de sanidade.

A invalidação do iterador se aplica a std::back_insert_iterators?

#include <cassert>
#include <iterator>
#include <vector>

int main() {
    auto v = std::vector<int>{ 0, 1, 2 };
    auto iter = std::back_inserter(v);
    *iter++ = 3;
    v.clear();    // invalidates iterators, but
    *iter++ = 4;  //  back_insert_iterator is special?
    assert(v.size() == 1 && v[0] == 4);
    return 0;
}

Este código funciona para mim porque a implementação std::back_insert_iterator do meu fornecedor não contém um iterador (ou ponteiro ou referência). Ele simplesmente chama o método push_back do contêiner.

Mas o padrão exige essa implementação? O back_insert_iterator de outro fornecedor poderia manter e manter um iterador one-past-the-end para usar com uma chamada para o método de inserção do contêiner? Parece que atenderia aos requisitos. Esta diferença, claro, é que seria vulnerável à invalidação.


Eu sei que cppreference.com não é oficial, mas é mais acessível que o padrão.

[Método claro de um vetor] [i]nvalida quaisquer ... iteradores referentes aos elementos contidos. Quaisquer iteradores além do final também são invalidados. [ cppreference.com , ênfase adicionada ]

Um std::back_insert_iterator pode ser o filho-propaganda de um iterador passado.

c++
  • 1 respostas
  • 50 Views

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Reformatar números, inserindo separadores em posições fixas

    • 6 respostas
  • Marko Smith

    Por que os conceitos do C++20 causam erros de restrição cíclica, enquanto o SFINAE antigo não?

    • 2 respostas
  • Marko Smith

    Problema com extensão desinstalada automaticamente do VScode (tema Material)

    • 2 respostas
  • Marko Smith

    Vue 3: Erro na criação "Identificador esperado, mas encontrado 'import'" [duplicado]

    • 1 respostas
  • Marko Smith

    Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores?

    • 1 respostas
  • Marko Smith

    Como faço para corrigir um erro MODULE_NOT_FOUND para um módulo que não importei manualmente?

    • 6 respostas
  • Marko Smith

    `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso?

    • 3 respostas
  • Marko Smith

    Um programa vazio que não faz nada em C++ precisa de um heap de 204 KB, mas não em C

    • 1 respostas
  • Marko Smith

    PowerBI atualmente quebrado com BigQuery: problema de driver Simba com atualização do Windows

    • 2 respostas
  • Marko Smith

    AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos

    • 1 respostas
  • Martin Hope
    Fantastic Mr Fox Somente o tipo copiável não é aceito na implementação std::vector do MSVC 2025-04-23 06:40:49 +0800 CST
  • Martin Hope
    Howard Hinnant Encontre o próximo dia da semana usando o cronógrafo 2025-04-21 08:30:25 +0800 CST
  • Martin Hope
    Fedor O inicializador de membro do construtor pode incluir a inicialização de outro membro? 2025-04-15 01:01:44 +0800 CST
  • Martin Hope
    Petr Filipský Por que os conceitos do C++20 causam erros de restrição cíclica, enquanto o SFINAE antigo não? 2025-03-23 21:39:40 +0800 CST
  • Martin Hope
    Catskul O C++20 mudou para permitir a conversão de `type(&)[N]` de matriz de limites conhecidos para `type(&)[]` de matriz de limites desconhecidos? 2025-03-04 06:57:53 +0800 CST
  • Martin Hope
    Stefan Pochmann Como/por que {2,3,10} e {x,3,10} com x=2 são ordenados de forma diferente? 2025-01-13 23:24:07 +0800 CST
  • Martin Hope
    Chad Feller O ponto e vírgula agora é opcional em condicionais bash com [[ .. ]] na versão 5.2? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench Por que um traço duplo (--) faz com que esta cláusula MariaDB seja avaliada como verdadeira? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng Por que `dict(id=1, **{'id': 2})` às vezes gera `KeyError: 'id'` em vez de um TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos 2024-03-20 03:12:31 +0800 CST

Hot tag

python javascript c++ c# java typescript sql reactjs html

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve