Инициализировать значения в структуре в функции C, используемой в качестве агрегатора SQLite - PullRequest
1 голос
/ 13 ноября 2010

У меня есть программа для расчета взвешенного продукта (я адаптировался из другой программы, которую нашел). Но я не могу понять, как инициализировать часть total_data структуры product_state единицей, чтобы я не всегда получал нулевой продукт. Сейчас он сохраняет нулевое значение по умолчанию, чтобы все значения, которые я передаю из SQLite, обнулялись.

Я взял немного C ++ в колледже, но никогда не углублялся в структуру, и возвращаться к этому - за пределами моей рубки. Спасибо!

/* product.c */

#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1;

#include <stdlib.h>

typedef struct product_state_s {
   double   total_data;  /* sum of (data * weight) values */
   double   total_wt;    /* sum of weight values */
} product_state;

static void product_step( sqlite3_context *ctx, int num_values, sqlite3_value **values )
{
    double         row_wt = 1.0;
    int            type;
    product_state   *st = (product_state*)sqlite3_aggregate_context( ctx,
                                               sizeof( product_state ) );
    if ( st == NULL ) {
        sqlite3_result_error_nomem( ctx );
        return;
    }

    /* Extract weight, if we have a weight and it looks like a number */
    if ( num_values == 2 ) {
        type = sqlite3_value_numeric_type( values[1] );
        if ( ( type == SQLITE_FLOAT )||( type == SQLITE_INTEGER ) ) {
            row_wt = sqlite3_value_double( values[1] );
        }
    }

    /* Extract data, if we were given something that looks like a number. */
    type = sqlite3_value_numeric_type( values[0] );
    if ( ( type == SQLITE_FLOAT )||( type == SQLITE_INTEGER ) ) {
        st->total_data *= row_wt * sqlite3_value_double( values[0] );
        st->total_wt   += row_wt;
    }
}

static void product_final( sqlite3_context *ctx )
{
    double         result = 0.0;
    product_state   *st = (product_state*)sqlite3_aggregate_context( ctx,
                                               sizeof( product_state ) );
    if ( st == NULL ) {
        sqlite3_result_error_nomem( ctx );
        return;
    }

    if ( st->total_wt != 0.0 ) {
        result = st->total_data / st->total_wt;
    }
    sqlite3_result_double( ctx, result );
}

int product_init( sqlite3 *db, char **error, const sqlite3_api_routines *api )
{
    SQLITE_EXTENSION_INIT2(api);

    sqlite3_create_function( db, "product", 1, SQLITE_UTF8,
            NULL, NULL, product_step, product_final );
    sqlite3_create_function( db, "product", 2, SQLITE_UTF8,
            NULL, NULL, product_step, product_final );

    return SQLITE_OK;
}

1 Ответ

1 голос
/ 13 ноября 2010

В product_step, после теста на st == NULL попробуйте добавить это:

if (0.0 == st->total_data) st->total_data = 1.0;

В общем, вы должны быть очень осторожны при сравнении значения с плавающей запятой на равенство. Математика с плавающей точкой ограничена в своей точности. Однако прямое сравнение должно работать нормально, при условии, что ни одно из значений в строке не равно нулю: в первый раз буфер, возвращаемый sqlite3_aggregate_context, обнуляется sqlite, поэтому значение должно сравниваться равным 0.0.

Если значение строки, равное нулю, возможно (и, скорее всего, так), вы должны добавить состояние в структуру, чтобы сказать, была ли выполнена инициализация:

typedef struct product_state_s {
   double   total_data;  /* sum of (data * weight) values */
   double   total_wt;    /* sum of weight values */
   bool     did_init;
} product_state;

и позже вместо проверки, равен ли st->total_data ноль, вместо:

if (!did_init) {
    st->total_data = 1.0;
    st->did_init = true;
}
...