为多种类型定义相同的函数时,如何避免在 C89 中重复自己?

问题描述 投票:0回答:1

我正在用 c89 编写一个简单的 dsp 库。使用此版本的语言以移植到旧机器是一个目标。我正在对我的库进行单元测试,想测量每个滤波器的振幅响应。我创建了以下使用 FFTW 库计算振幅响应的方法。

#include "algaec.h"
#include <fftw3.h>

void algae__biquad_compute_amplitude_response(algae__sample_t *amplitude_response,
                                const size_t number_of_bins,
                                algae__biquad_t *filter,
                                const algae__frequency_t sample_rate,
                                const size_t blocksize) {

  algae__sample_block_empty(amplitude_response, blocksize);
  enum complex { RE, IM };
  const size_t N = 2 * number_of_bins;
  fftw_complex *in;
  fftw_complex *out;
  fftw_plan p;

  in = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * N);
  out = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * N);
  p = fftw_plan_dft_1d(N, in, out, FFTW_FORWARD, FFTW_ESTIMATE);

  algae__sample_t impulse[blocksize];
  algae__sample_block_empty(impulse, blocksize);
  impulse[0] = 1;
  algae__biquad_process(filter, impulse, impulse, blocksize);
  size_t idx;
  for (idx = 0; idx < N; idx++) {
    in[idx][RE] = impulse[idx];
    in[idx][IM] = 0;
  }

  fftw_execute(p);

  for (idx = 0; idx < number_of_bins; idx++) {
    amplitude_response[idx] =
        (sqrt(out[idx][RE] * out[idx][RE] + out[idx][IM] * out[idx][IM]));
  }

  fftw_destroy_plan(p);
  fftw_free(in);
  fftw_free(out);
}

我还想测试一个单极 IIR 滤波器。为此,我现在需要定义以下内容:


void algae__onepole_compute_amplitude_response(algae__sample_t *amplitude_response,
                                const size_t number_of_bins,
                                algae__onepole_t *filter,
                                const algae__frequency_t sample_rate,
                                const size_t blocksize) {

  algae__sample_block_empty(amplitude_response, blocksize);
  enum complex { RE, IM };
  const size_t N = 2 * number_of_bins;
  fftw_complex *in;
  fftw_complex *out;
  fftw_plan p;

  in = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * N);
  out = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * N);
  p = fftw_plan_dft_1d(N, in, out, FFTW_FORWARD, FFTW_ESTIMATE);

  algae__sample_t impulse[blocksize];
  algae__sample_block_empty(impulse, blocksize);
  impulse[0] = 1;
  algae__onepole_process(filter, impulse, impulse, blocksize);
  size_t idx;
  for (idx = 0; idx < N; idx++) {
    in[idx][RE] = impulse[idx];
    in[idx][IM] = 0;
  }

  fftw_execute(p);

  for (idx = 0; idx < number_of_bins; idx++) {
    amplitude_response[idx] =
        (sqrt(out[idx][RE] * out[idx][RE] + out[idx][IM] * out[idx][IM]));
  }

  fftw_destroy_plan(p);
  fftw_free(in);
  fftw_free(out);
}

最终我会有更多的过滤器类型。我想测试所有这些的振幅响应,以确保我正确地实现了它们。如果我使用的是 C++,我可以使用模板使

algae__compute_amplitude_response
algae__process
在过滤器类型方面通用化,并让编译器自动生成正确的实现,同时我只需要维护一个代码路径。有什么办法可以避免在这里重复我自己吗?

到目前为止,我已经研究了以下选项。他们似乎都没有吸引力......

  1. 我使用预处理器宏来定义一个通用的
    process
    方法和一个通用的
    compute_amplitude_response
    方法......我不完全清楚这将如何工作并担心它会对我的生产代码的可读性产生什么影响。
  2. 我创建了我的不同过滤器结构的标记联合,并创建了一个
    process
    方法来识别结构的类型,然后重定向到正确的方法......为了测试,这会影响我的生产代码的结构(不一定会破坏交易,但有点难过)。它还会影响我的生产代码的实现和性能,因为联合结构将占用与其最大成员相同的内存量。对于单极滤波器与双二阶滤波器,这是 2 个浮点数和 9 个浮点数之间的区别。
  3. 空指针有些可怕???也许定义一个 struct 是 void 指针和标记它的类型的组合。这感觉就像打开一个罐头蠕虫,可能会使我的生产 API 非常混乱。

我在这里缺少任何选项吗?-

c c89
1个回答
0
投票

您可以使用函数指针来做到这一点,但首先您需要确保相关函数具有相同的签名类型。

首先,更改

algae__onepole_process
algae__biquad_process
,使每个参数的第一个参数具有
void *
类型,即:

void algae__onepole_process(void *filter, algae__sample_t *impulse1, 
                            algae__sample_t *impulse2, size_t blocksize);
void algae__biquad_process(void *filter, algae__sample_t *impulse1, 
                           algae__sample_t *impulse2, size_t blocksize);

这意味着您需要将每个中的

filter
参数复制到适当类型的指针。

然后创建一个函数来完成上述两个操作,将

filter
参数的类型更改为
void *
并为要调用的过程函数添加函数指针:

void algae__generic_compute_amplitude_response(algae__sample_t *amplitude_response,
            const size_t number_of_bins,
            void (*process)(void *, algae__sample_t *, algae__sample_t *, size_t),
            void *filter,
            const algae__frequency_t sample_rate,
            const size_t blocksize) {

然后将使用函数指针而不是特定函数:

process(filter, impulse, impulse, blocksize);
© www.soinside.com 2019 - 2024. All rights reserved.