【问题标题】:Multiple definitions for function templates with float, trying to resolve linker error带有浮点数的函数模板的多个定义,试图解决链接器错误
【发布时间】:2021-05-23 02:49:20
【问题描述】:

我正在开发一组函数模板并且编译得很好。您可以在 Compiler Explorer 上看到它们在 here 上工作...问题是当我尝试在我的项目中将它与 T = float 一起使用时,由于多个定义而无法链接...我尝试了各种事情和似乎无法解决这个问题...

这是我的头文件:

cstr.h

#pragma once

#include <stdint.h>

const uint8_t default_decimal_places = 8;

template<typename T>
const char* to_string(T value) {
    static char output_buffer[128];

    uint8_t negative = 0;
    bool isNegative = false;
    if (value < 0) {
        isNegative = true;
        negative = 1;
        value *= -1;
        output_buffer[0] = '-';
    }

    uint8_t size = 0;
    T sizeTest = value;
    while (sizeTest / 10 > 0) {
        sizeTest /= 10;
        size++;
    }

    uint8_t idx = 0;
    if (isNegative) {
        while (value / 10 > 0) {
            uint8_t remainder = value % 10;
            value /= 10;
            output_buffer[negative + size - idx] = remainder + '0';
            idx++;
        }
        uint8_t remainder = value % 10;
        output_buffer[negative + size - idx] = remainder + '0';
        output_buffer[negative + size + 1] = 0;
    }
    else {
        while (value / 10 > 0) {
            uint8_t remainder = value % 10;
            value /= 10;
            output_buffer[size - idx] = remainder + '0';
            idx++;
        }
        uint8_t remainder = value % 10;
        output_buffer[size - idx] = remainder + '0';
        output_buffer[size + 1] = 0;
    }

    return output_buffer;
}

template<typename T>
const char* to_string(T value, uint8_t decimal_places) {
    if (decimal_places > 20) decimal_places = 20;

    static char output_double_buffer[128];
    char* intPtr = (char*)to_string((int64_t)value);
    char* doublePtr = output_double_buffer;

    if (value < 0) {
        value *= -1;
    }

    while(*intPtr != 0) {
        *doublePtr = *intPtr;
        intPtr++;
        doublePtr++;
    }

    *doublePtr = '.';
    doublePtr++;

    T newValue = value - (int)value;

    for (uint8_t i = 0; i < decimal_places; i++) {
        newValue *= 10;
        *doublePtr = (int)newValue + '0';
        newValue -= (int)newValue;
        doublePtr++;
    }

    *doublePtr = 0;
    return output_double_buffer;
}

template<>
inline const char* to_string<float>(float value, uint8_t decimal_places) {
    if (decimal_places > 14) decimal_places = 14;

    static char output_float_buffer[128];
    char* intPtr = (char*)to_string((int64_t)value);
    char* floatPtr = output_float_buffer;

    if (value < 0) {
        value *= -1;
    }

    while(*intPtr != 0) {
        *floatPtr = *intPtr;
        intPtr++;
        floatPtr++;
    }

    *floatPtr = '.';
    floatPtr++;

    float newValue = value - (int)value;

    for (uint8_t i = 0; i < decimal_places; i++) {
        newValue *= 10;
        *floatPtr = (int)newValue + '0';
        newValue -= (int)newValue;
        floatPtr++;
    }

    *floatPtr = 'f';
    floatPtr++;
    *floatPtr = 0;
    return output_float_buffer;
}

它是 cpp 文件...

#include "cstr.h"

调用它们的类...

BasicRenderer.cpp

void BasicRenderer::Print(double val, uint8_t decimal_places) {
    Print(to_string(val, decimal_places));
}

void BasicRenderer::Print(float val, uint8_t decimal_places) {
    Print(to_string(val, decimal_places));
}

但是,当我在我的应用程序中运行它时... GCC 给了我这个链接器错误...

skilz420@skilz-PC:~/skilzOS/kernel$ make kernel
!==== COMPILING src/cstr.cpp
gcc -ffreestanding -fshort-wchar -c src/cstr.cpp -o lib/cstr.o
!==== LINKING
ld -T kernel.ld -static -Bsymbolic -nostdlib -o bin/kernel.elf  lib/kernel.o  lib/cstr.o  lib/BasicRenderer.o
ld: lib/BasicRenderer.o: in function `char const* to_string<float>(float, unsigned char)':
BasicRenderer.cpp:(.text+0x0): multiple definition of `char const* to_string<float>(float, unsigned char)'; lib/kernel.o:kernel.cpp:(.text+0x0): first defined here
make: *** [Makefile:33: link] Error 1
skilz420@skilz-PC:~/skilzOS/kernel$ 

我什至尝试将它的定义移动到CPP 文件中,将其内联,等等......这是一个漫长的夜晚,我只想在我称之为夜晚之前让它工作......double本身工作正常,但是当我尝试添加并专门化 float 变体时会这样做,是的,float 的行为与 double 不同。

【问题讨论】:

  • 您可以考虑使用不同的名称,例如to_cstring,以避免与返回std::string 的std::to_string 发生冲突。另外,我猜这些是为了“立即”使用,因为返回的指针指向一个 static 局部变量,但是您是否考虑过更类似于 std::to_chars 的接口?
  • @Bob__ 我实际上正在关注一个视频系列,并且我正在接近视频中的实现......在他们开始为每种类型编写相同的函数之后......我只是试过模板化这些函数......使用这些函数的 Print 函数现在实际上属于一个类......所以不应该与 stl 函数发生“冲突”。到目前为止,我没有在使用它们的类集之外使用这些函数,我们也没有使用stl,因为它是从头开始编写自定义操作系统的系列......
  • @super 是的,我忘了添加调用这些的类函数......我离开电脑,喝了一杯然后回来......然后我意识到了什么导致链接错误...您可以从我下面的回答中看到。
  • @super 我还添加了调用这些函数模板的函数,它们处于我能够修复它们之前的状态......

标签: c++ templates gcc linker-errors multiple-definition-error


【解决方案1】:

好的,喝了一杯回来后...我发现了问题所在...在头文件中的方式很好。问题出在另一个正在使用它的类中。比如……

void BasicRenderer::Print(const char* str ) {
    // code implementation
}

void BasicRenderer::Print(double val, uint8_t decimal_places) {
    Print(to_string(val, decimal_places);
}

void BasicRenderer::Print(float val, uint8_t decimal_places);
    Print(to_string(val, decimal_places);
}

这就是导致链接器错误的原因......

我不得不将它们更改为以下...

void BasicRenderer::Print(double val, uint8_t decimal_places) {
    Print(to_string<double>(val, decimal_places);
}

void BasicRenderer::Print(float val, uint8_t decimal_places);
    Print(to_string<float>(val, decimal_places);
}

现在它们都可以工作了。我必须显式地实例化它们......这发生在我将代码重构到我开始模板化这些转换和打印函数的地方......几乎一个小时我一直盯着这个问题挠头...... --eh-- template&lt;T = float&gt; = headaches...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-04-01
    • 1970-01-01
    • 2011-07-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-01
    相关资源
    最近更新 更多