Как я могу распечатать void *, что я не знаю тип данных? - PullRequest
0 голосов
/ 25 апреля 2020

У меня есть структура с двумя разными std::multimap. Я хотел бы сделать что-то вроде этого, но как я могу распечатать void*, который я не знаю, тип данных?

struct Data{
  std::multimap<uint64_t,uint64_t, std::greater<uint64_t>> b;
  std::multimap<uint64_t,uint64_t, std::less<uint64_t>> s;
};

Data data;

void do_something(bool c){
    void* pointer;
    uint64_t cumulative = 0;
    if(c){
        pointer = &data.b;
    } else {
        pointer = &data.s;
    }
    /*      Here I don't know if pointer is
    *           std::multimap<uint64_t,uint64_t, std::greater<uint64_t>>
    *           or
    *           std::multimap<uint64_t,uint64_t, std::less<uint64_t>>
    */
    for(auto it = (*pointer).begin(); it != (*pointer).end(); ++it){ 
        cumulative += it->second;
    }

}

Спасибо

Ответы [ 4 ]

0 голосов
/ 25 апреля 2020

Только с void* вы не можете. Однако есть два возможных решения (с разными уровнями интрузивности):

  1. Вы также можете кодировать тип некоторым способом, например, с помощью enum, и использовать его для static_cast обратно к конкретному типу. .
struct A {};
struct B {};

enum class Type
{
    A,
    B,
};

void do_something(bool c)
{
    A a;
    B b;

    void* pointer;
    Type type;

    if (c)
    {
        pointer = &a;
        type = Type::A;
    }
    else
    {
        pointer = &b;
        type = Type::B;
    }

    if (type == Type::A)
    {
        A* concrete = static_cast<A*>(pointer);
        // Use concrete here as A.
    }
    else if (type == Type::B)
    {
        B* concrete = static_cast<B*>(pointer);
        // Use concrete here as B.
    }
}
Сделайте ваши типы полиморфными c (с виртуальными функциями и общим базовым классом). Вместо void* сделайте pointer указателем на общий базовый класс, и вы можете dynamic_cast на конкретный тип.

Я бы отметил, однако, что обычно в хорошо разработанной программе, polymorphi c использование типов не должно когда-либо знать конкретный тип. Изучите использование виртуальных функций или std::variant.

0 голосов
/ 25 апреля 2020

Без дополнительной информации вы не узнаете, что это за базовый тип. Следовательно, приведение void* к любому типу указателя типа подвержено ошибкам.

Вы можете справиться с ситуацией, используя другую стратегию.

template <typename Iter>
uint64_t do_something(Iter start, Iter end)
{
   uint64_t cumulative = 0;
   for(auto it = start; it != end; ++it){ 
      cumulative += it->second;
   }
   return cumulative;
}

void do_something(bool c)
{
   uint64_t cumulative = 0;
   if(c)
   {
      cumulative = do_something(data.b.begin(), data.b.end());
   }
   else
   {
      cumulative = do_something(data.s.begin(), data.s.end());
   }
}

Вы можете использовать std::accumulate для упрощения первой функции.

template <typename Iter>
uint64_t do_something(Iter start, Iter end)
{
   return std::accumulate(start, end, 0);
}
0 голосов
/ 25 апреля 2020

Вы не можете разыменовать void-указатель, но в вашем случае вы можете сделать так:

template<typename Container>
uint64_t do_something_helper(const Container& container)
{
    uint64_t cumulative = 0;
    for (const auto& item : container) {
        cumulative += item.second;
    }
    return cumulative;
}

void do_something(bool c)
{
    uint64_t cumulative = [&]() {
        if (c) {
            return do_something_helper(data.b);
        }
        else {
            return do_something_helper(data.s);
        }
    }();

    // Do something with cumulative
}

Более того. Вот std::any в C ++ 17:

void do_something(bool c)
{
    std::any container;
    if (c) {
        container = data.b;
    }
    else {
        conainter = data.s;
    }

    try {
        auto* pCont = std::any_cast<decltype(data.b)>( &container );
        for ( ... ) {
            // compute cumulative
        }
    }
    catch(const std::bad_any_cast&) {
        auto* pCont = std::any_cast<decltype(data.s)>( &container );
        for ( ... ) {
            // compute cumulative
        }
    }
}

Но для меня первое решение выглядит немного лучше, чем второе:)

0 голосов
/ 25 апреля 2020

Как я могу разослать пустоту *, что я не знаю тип данных?

Вы не могли бы сделать это.

Вы можете привести только к введите, что вы знаете.

Для чего-то подобного вы можете написать шаблон функции, который работает в любом диапазоне:

template<class Range>
void do_something_template(Range& r){
    // do something
}

void do_something(bool c){
    uint64_t cumulative = c
        ? do_something_template(data.b)
        : do_something_template(data.s);

Тем не менее, уже существует стандартный алгоритм для того, кто вы есть делать в данном конкретном случае: std::accumulate и перезаписывать его не нужно.

...