Как получить двойное значение с помощью libpq?

примеры в документации по libpq показывают, как получить целое число значение путем преобразования его в представление host-endian.

Мне любопытно, что нужно сделать, чтобы получить значение двойной точности с использованием libpq (без libpqtyppes)? Я пробовал reinterpret_cast, но безуспешно.

Также почему текстовые и байтовые данные не нуждаются в обратном преобразовании?

БД работает локально в Windows 7, я использую Visual C ++ 2013.

pptr - это двойное слово, которое я пытаюсь извлечь.

#include <iostream>
#include <memory>
#include <vector>
#include <libpq-fe.h>
#include <Winsock2.h>


static void
show_binary_results(PGresult *res)
{
    int i, j;
    int i_fnum, n_fnum, p_fnum;

    /* Use PQfnumber to avoid assumptions about field order in result */
    i_fnum = PQfnumber(res, "id");
    n_fnum = PQfnumber(res, "name");
    p_fnum = PQfnumber(res, "price");

    for (i = 0; i < PQntuples(res); i++)
    {
        char* iptr;
        char* nptr;
        char* pptr;
        int         blen;
        int         ival;

        /* Get the field values (we ignore possibility they are null!) */
        iptr = PQgetvalue(res, i, i_fnum);
        nptr = PQgetvalue(res, i, n_fnum);
        pptr = PQgetvalue(res, i, p_fnum); /*THIS IS A VALUE I AM TRYING TO GET*/

        /*
        * The binary representation of INT4 is in network byte order, which
        * we'd better coerce to the local byte order.
        */
        ival = ntohl(*((uint32_t *) iptr));

        /*
        * The binary representation of TEXT is, well, text, and since libpq
        * was nice enough to append a zero byte to it, it'll work just fine
        * as a C string.
        *
        * The binary representation of BYTEA is a bunch of bytes, which could
        * include embedded nulls so we have to pay attention to field length.
        */
        //blen = PQgetlength(res, i, b_fnum);

        printf("tuple %d: got\n", i);
        printf(" i = (%d bytes) %d\n",
            PQgetlength(res, i, i_fnum), ival);
        printf(" t = (%d bytes) '%s'\n",
            PQgetlength(res, i, n_fnum), nptr);
        printf(" p = (%d bytes) %f\n",
            PQgetlength(res, i, p_fnum), *reinterpret_cast<double*>(pptr));

        printf("\n\n");
    }
}


int main(int argc, char* argv [])
{
    auto conn_string = "postgresql://postgres:pwd@localhost/db";
    auto conn_deleter = [](PGconn* c) { PQfinish(c); };
    auto res_deleter = [](PGresult* r) { PQclear(r); std::cout << "deleted" << std::endl; };
    std::unique_ptr<PGconn, decltype(conn_deleter)> conn(PQconnectdb(conn_string), conn_deleter);

    if (PQstatus(conn.get()) != ConnStatusType::CONNECTION_OK)
        std::cerr << "Problem" << std::endl;

    std::vector<const char *> params{ "1" };
    std::unique_ptr < PGresult, decltype(res_deleter)> res(PQexecParams(conn.get(),
        "SELECT * FROM table_with_double WHERE id = $1",
        params.size(),       /* one param */
        NULL,    /* let the backend deduce param type */
        (const char * const *)&params.front(),
        NULL,    /* don't need param lengths since text */
        NULL,    /* default to all text params */
        1), /* ask for binary results */
        res_deleter);      

    if (PQresultStatus(res.get()) != ExecStatusType::PGRES_TUPLES_OK)
        std::cout << "SELECT failed: " << PQerrorMessage(conn.get()) << std::endl;
    show_binary_results(res.get());
}

person Kimi    schedule 10.12.2013    source источник
comment
Как вы вызываете PQgetvalue?   -  person Stephane Rolland    schedule 11.12.2013
comment
AFAIK, text и bytea в основном прославляются char* на этом уровне, поэтому нет необходимости беспокоиться о порядке байтов. Точно так же битовая компоновка двойника IEEE не зависит от машины, поэтому здесь тоже нет байта.   -  person mu is too short    schedule 11.12.2013
comment
@muistooshort, это то, что я ожидал, но двойное значение, которое я получаю, равно 0.   -  person Kimi    schedule 11.12.2013
comment
@StephaneRolland, я выложил код.   -  person Kimi    schedule 11.12.2013
comment
здесь вы делаете atof, чтобы попытаться преобразовать значение результата, это не приведение! не могли бы вы опубликовать код, связанный с вашим вопросом, то есть с reinterpret_cast to float, который вы пробовали?   -  person Stephane Rolland    schedule 11.12.2013
comment
@StephaneRolland, под редакцией. Я пробовал много глупостей, но безуспешно. Ясно, что я не знаю, что делать.   -  person Kimi    schedule 11.12.2013
comment
не могли бы вы проверить, что PQgetlength(res, i, p_fnum); равно 4 на случай, если возникнут проблемы с определением вашего столбца? В противном случае, проверяли ли вы в своей базе данных, действительно ли в вашей записи есть поле, отличное от 0.0 для этой записи (распечатайте идентификатор, чтобы убедиться, что он указывает на хороший результат)?   -  person Stephane Rolland    schedule 11.12.2013
comment
вот вопрос, в котором я спросил о проблемах с порядком байтов с двойными числами в libpq. Ответы могут вам помочь. stackoverflow.com/q/15079463/356440.   -  person Stephane Rolland    schedule 11.12.2013
comment
извините, я сделал ошибку, вы должны проверить PQgetlength (res, i, p_fnum); равно 8, а не 4, так как вы запрашиваете двойное значение, а не float.   -  person Stephane Rolland    schedule 11.12.2013
comment
@StephaneRolland, длина 8 байт, это двойное число с плавающей запятой 64 бита. Поля int и string извлекаются правильно, поэтому запись верна.   -  person Kimi    schedule 11.12.2013


Ответы (1)


По-видимому, данные для двойного столбца поступают с прямым порядком байтов и должны быть преобразованы в прямой порядок байтов. То же, что и ints. На основе этого превосходного статья из этого ответа Я использовал простую функцию для замены двойного значения.

double double_swap(double d)
{
    union
    {
        double d;
        unsigned char bytes[8];
    } src, dest;

    src.d = d;
    dest.bytes[0] = src.bytes[7];
    dest.bytes[1] = src.bytes[6];
    dest.bytes[2] = src.bytes[5];
    dest.bytes[3] = src.bytes[4];
    dest.bytes[4] = src.bytes[3];
    dest.bytes[5] = src.bytes[2];
    dest.bytes[6] = src.bytes[1];
    dest.bytes[7] = src.bytes[0];
    return dest.d;
}

Применяя эту функцию, правильное значение извлекается из БД.

printf(" p = (%d bytes) %lf\n",
            PQgetlength(res, i, p_fnum), double_swap(*((double*) pptr)));
person Kimi    schedule 11.12.2013
comment
пожалуйста, также подумайте об использовании std::cout <<, а не printf. Он считается C ++ вместо C. Он также выполняет все обычные преобразования на лету. - person Stephane Rolland; 12.12.2013
comment
@StephaneRolland, это код из примеров libpq, таким образом я хочу показать, что не делаю ничего необычного. Я согласен с тем, что этот код не должен проходить проверку кода. - person Kimi; 12.12.2013
comment
Поскольку OP явно упомянул MS Windows (но не 8), существует htonll для замены порядка байтов на 64-битные целые числа / слепые двойные числа. - person mlt; 19.05.2015