Ошибка связи с действительно простыми функциями C ++ в файле .h - PullRequest
5 голосов
/ 21 июня 2011

Я сделал две функции для «преобразования» 32/64-битного указателя в double.Код работал, когда использовался один (только .h и .cpp, включая его), но при использовании .h где-то еще (копировал в каталог проекта и затем включал), он выдает ошибку «уже определено» для всех функций в.h файл при компоновке.

исходный код для файла .h следующий:

#pragma once
#ifndef __FLOATCAST_H
#define __FLOATCAST_H

//A quick and dirty way of casting pointers into doubles and back
//Should work with BOTH 64bit and 32bit pointers
union ptr_u {
    double d;
    void* p;
};

double ptr2double(void* pv){
    ptr_u ptr;
    ptr.p = pv;
    return (ptr.d);
};

void* double2ptr(double dv){
    ptr_u ptr;
    ptr.d = dv;
    return(ptr.p);
};

#endif

Ссылка говорит, что функции уже определены в исходном файле файла (точнее, его.obj), который не включает этот.

Редактировать: Зачем мне нужен указатель внутри-двойной?Поскольку мне нужен Lua (5.1) для обратного вызова функции-члена объекта.

Edit2: Lua предоставляет способ хранения пользовательских данных, похоже, что это адекватное решение, а не приведение указателя (см. Комментарии)

Ответы [ 4 ]

8 голосов
/ 21 июня 2011

Отметьте свои функции как встроенные

inline double ptr2double(void* pv){
    ptr_u ptr;
    ptr.p = pv;
    return (ptr.d);
};

inline void* double2ptr(double dv){
    ptr_u ptr;
    ptr.d = dv;
    return(ptr.p);
};

Видите ли, когда вы включили одну и ту же функцию в два отдельных исходных файла (единицы перевода), вы получите несколько определений. Обычно у вас есть 2 варианта:

  • Содержит описания ваших функций в файле .h и определения в отдельном файле .cpp
  • Сделайте ваши функции встроенными и сохраните их в файле .h

Правило с одним определением C ++ запрещает множественные определения не встроенных функций.

EDIT: #ifdef защищает от множественного включения в одиночный исходный файл. Но вы действительно можете включить файл .h в разные файлы .cpp. ODR применяется к определениям всей программы, а не только одного файла.

EDIT2 После некоторых комментариев я чувствую, что должен включить сюда эту информацию, чтобы не было недопонимания. В C ++ существуют разные правила, касающиеся встроенных и не встроенных функций, например, особый случай ODR. Теперь вы можете пометить любую функцию (будь она длинной или рекурсивной, не имеет значения) как встроенную, и к ним будут применяться специальные правила. Это совершенно другой вопрос, решит ли компилятор на самом деле встроить его (то есть заменить код вместо вызова), что он может сделать, даже если вы не помечаете функцию как встроенную и можете решить не делать этого даже если вы отметите его как встроенный.

3 голосов
/ 21 июня 2011

Каноническим способом будет:

floatcast.h

#pragma once
#ifndef __FLOATCAST_H
#define __FLOATCAST_H

//A quick and dirty way of casting pointers into doubles and back
//Should work with BOTH 64bit and 32bit pointers
union ptr_u {
    double d;
    void* p;
};

double ptr2double(void* pv);
void* double2ptr(double dv);

#endif

floatcast.cpp

#include "floatcast.h"

double ptr2double(void* pv){
    ptr_u ptr;
    ptr.p = pv;
    return (ptr.d);
};

void* double2ptr(double dv){
    ptr_u ptr;
    ptr.d = dv;
    return(ptr.p);
};    
0 голосов
/ 21 июня 2011

Вы получаете ошибку, потому что функции компилируются дважды (каждый раз, когда включается заголовок), и поэтому компоновщик, который объединяет все объектные файлы в конечный исполняемый файл, видит несколько определений функций.* Как писал Армен, одно из решений - сделать функции встроенными.Таким образом, код для функций всегда копируется всякий раз, когда они используются.Это реальное решение здесь, потому что функции настолько малы, что код не будет сильно раздутым.Если у вас есть какие-то большие функции в заголовочном файле или вам нужно лучше организовать свой код, вы должны взять код из заголовочного файла и вставить в файл cpp (как в ответе sehe).

0 голосов
/ 21 июня 2011

Поместите свой код в файл .cpp и в файл .h просто поместите прототипы -

double ptr2double(void* pv);
void* double2ptr(double dv);

Или оставьте код таким, какой он есть, и добавьте «inline» в каждое определение функции, которое позволитВы должны определить их в каждом модуле

Хотя я не думаю, что ваш код действительно сделает что-то очень полезное.Это ненадежный способ доступа к члену объединения, отличному от того, который вы использовали для хранения данных.Плюс, даже если это «сработало», я не могу думать, как вы планируете использовать это, вы сохраните двойной тип и прочитаете 32-битные из них в указатель (на 32-битной машине) ... Как вы можете использоватьэто для чего-нибудь?

...