Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Статическая C библиотека в С++
Форум на CrossPlatform.RU > Разработка > С\С++
AD
Можно ли использовать статическую библиотеку, написанную на C, в проекте на С++? Сделал эксперимент: пересобрал библиотеку компилятором С++, подсунул в проект - ошибок линковки не выдает, а когда сую С библиотеку - ошибки линкования. Можно ли так делать? Если можно, то как именно?
Iron Bug
используй
extern "C"

в заголовке библитотеки
AD
Цитата(Iron Bug @ 14.3.2014, 8:55) *
используй
extern "C"

в заголовке библитотеки

Не помогло. Видимо, еще какие-то настройки нужны.... :(
Влад
А какие именно "ошибки линкования" выдает? Приведи полный текст сообщения об ошибке(-ах). Возможно, текст поможет понять, что не у тебя так.
AD
Цитата(Влад @ 17.3.2014, 14:06) *
А какие именно "ошибки линкования" выдает? Приведи полный текст сообщения об ошибке(-ах). Возможно, текст поможет понять, что не у тебя так.

К сожалению, я это смогу сделать только через несколько дней. Но вообще самые типичные ошибки лигкования: типа не найдена реализация такой-то функции.
Iron Bug
кроме extern "C" ничего не надо. а про ошибки линковки: возможно, сама библиотека что-то ещё подтаскивает и это не прилинковано.
AD
Цитата(Iron Bug @ 18.3.2014, 8:26) *
кроме extern "C" ничего не надо. а про ошибки линковки: возможно, сама библиотека что-то ещё подтаскивает и это не прилинковано.

Ну не знаю уж. Вот полное описание ошибок:

Цитата
...Projects/fpo/mztf/bulat.ya1/kc-logger # make
make -j 1 -Cx86 -fMakefile
make[1]: Entering directory `/home/ad/Projects/fpo/mztf/bulat.ya1/kc-logger/x86'
make -j 1 -Co -fMakefile
make[2]: Entering directory `/home/ad/Projects/fpo/mztf/bulat.ya1/kc-logger/x86/o'
/usr/qnx650/host/qnx6/x86/usr/bin/qcc -Vgcc_ntox86 -c -Wc,-Wall -O -O3 -DNDEBUG -I. -I/home/ad/Projects/fp
o/mztf/bulat.ya1/kc-logger/x86/o -I/home/ad/Projects/fpo/mztf/bulat.ya1/kc-logger/x86 -I/home/ad/Projects/fpo/mztf/bu
lat.ya1/kc-logger -I/usr/qnx650/target/qnx6/usr/include -DBUILDENV_qss /home/ad/Projects/fpo/mztf/bulat.ya1/kc-l
ogger/bit_operations.cpp
/usr/qnx650/host/qnx6/x86/usr/bin/qcc -Vgcc_ntox86 -c -Wc,-Wall -O -O3 -DNDEBUG -I. -I/home/ad/Projects/fp
o/mztf/bulat.ya1/kc-logger/x86/o -I/home/ad/Projects/fpo/mztf/bulat.ya1/kc-logger/x86 -I/home/ad/Projects/fpo/mztf/bu
lat.ya1/kc-logger -I/usr/qnx650/target/qnx6/usr/include -DBUILDENV_qss /home/ad/Projects/fpo/mztf/bulat.ya1/kc-l
ogger/config_reader.cpp
/usr/qnx650/host/qnx6/x86/usr/bin/qcc -Vgcc_ntox86 -c -Wc,-Wall -O -O3 -DNDEBUG -I. -I/home/ad/Projects/fp
o/mztf/bulat.ya1/kc-logger/x86/o -I/home/ad/Projects/fpo/mztf/bulat.ya1/kc-logger/x86 -I/home/ad/Projects/fpo/mztf/bu
lat.ya1/kc-logger -I/usr/qnx650/target/qnx6/usr/include -DBUILDENV_qss /home/ad/Projects/fpo/mztf/bulat.ya1/kc-l
ogger/main.cpp
/usr/qnx650/host/qnx6/x86/usr/bin/qcc -Vgcc_ntox86 -c -Wc,-Wall -O -O3 -DNDEBUG -I. -I/home/ad/Projects/fp
o/mztf/bulat.ya1/kc-logger/x86/o -I/home/ad/Projects/fpo/mztf/bulat.ya1/kc-logger/x86 -I/home/ad/Projects/fpo/mztf/bu
lat.ya1/kc-logger -I/usr/qnx650/target/qnx6/usr/include -DBUILDENV_qss /home/ad/Projects/fpo/mztf/bulat.ya1/kc-l
ogger/str_functions.cpp
/bin/rm -f /home/ad/Projects/fpo/mztf/bulat.ya1/kc-logger/x86/o/kc-logger
/usr/qnx650/host/qnx6/x86/usr/bin/qcc -Vgcc_ntox86 -lang-c++ -lang-c++ -Wl,-S -o/home/ad/Projects/fpo/mztf/bulat.y
a1/kc-logger/x86/o/kc-logger bit_operations.o config_reader.o main.o str_functions.o -L . -L /home/ad/P
rojects/fpo/mztf/bulat.ya1/kc-logger/Libraries/x86/a -L /usr/qnx650/target/qnx6/x86/lib -L /usr/qnx650/target/qnx6/x8
6/usr/lib -Wl,--rpath-link . -Wl,--rpath-link /home/ad/Projects/fpo/mztf/bulat.ya1/kc-logger/Libraries/x86/a -Wl,--r
path-link /usr/qnx650/target/qnx6/x86/lib -Wl,--rpath-link /usr/qnx650/target/qnx6/x86/usr/lib -Bstatic -llogge
r -lpulsar_socket -lpulsar -Bdynamic -lmenu -lpanel -lncurses -lsocket
main.o: In function `main':
main.cpp:(.text+0x31): undefined reference to `lmsg_init(char const*, LoggerLevel, unsigned int, unsigned int, char*)
'
main.cpp:(.text+0x91): undefined reference to `lmsg_log(LoggerLevel, unsigned int, char const*, ...)'
main.cpp:(.text+0xad): undefined reference to `lmsg_log(LoggerLevel, unsigned int, char const*, ...)'
main.cpp:(.text+0xb2): undefined reference to `lmsg_close()'
cc: /usr/qnx650/host/qnx6/x86/usr/bin/ntox86-ld error 1
make[2]: *** [/home/ad/Projects/fpo/mztf/bulat.ya1/kc-logger/x86/o/kc-logger] Error 1
make[2]: Leaving directory `/home/ad/Projects/fpo/mztf/bulat.ya1/kc-logger/x86/o'
make[1]: *** [all] Error 2
make[1]: Leaving directory `/home/ad/Projects/fpo/mztf/bulat.ya1/kc-logger/x86'
make: *** [all] Error 2


Вот код библиотеки.
Header:
/** logger.h
* This file describes the log-functions and variables
**/

#ifndef LOGGER_H_05550
#define LOGGER_H_05550

#include <syslog.h>
#include <sys/types.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdint.h>

#ifdef __cplusplus
  extern "C" {
#endif

#define MAX_APP_NAME_LEN 1024

typedef uint32_t LoggerVerbosityLevel;        ///< in debug each loop level should increment it

typedef enum { LogEmerg = 0, LogAlert, LogCrit, LogErr, LogWarning, LogNotice, LogInfo, LogDebug } LoggerLevel;

#define LOG_OUTPUT_NONE 0
#define LOG_OUTPUT_SYSLOG 1
#define LOG_OUTPUT_TTY 2
/// 3, 7, 15, 31 and etc is not defined, because in binary mode they are consist from units
#define LOG_OUTPUT_FILE 4

#define LOGGER_OUTPUT_MASK 0x7

/// Structure of the log message
typedef struct
{
    LoggerLevel level;        ///< log level of the message
    char* header;            ///< header of the message
    char* text;                ///< text of the message
    FILE* file;                ///< possibility of writing to file
} LMessage;

/**
* @brief Initialization the logger
* @param app_name - name of the application
* @param log_level - level of the log message
* @param verbosity - type of the log verbosity
* @param dev - output device (tty, or file, or syslog)
* @param file_name - name of the file (if the file is exist)
**/
void lmsg_init(const char* app_name, LoggerLevel log_level, LoggerVerbosityLevel
               verbosity, uint32_t dev, char* file_name);

/**
* @brief Prints the message to an output device
* @param log_level - level of the log message
* @param verbosity - type of the log verbosity
* @param message - outputing message
**/
void lmsg_log(LoggerLevel log_level, LoggerVerbosityLevel verbosity,
              const char* message, ...);

/** @brief Closes the logger output and free memory **/
void lmsg_close();

#ifdef __cplusplus
};
#endif

#endif //LOGGER_H_05550


Realization:
#include "logger.h"

#include <string.h>
#include <time.h>
#include <pthread.h>
#include <stdlib.h>

/// @name Declaration
/**@{*/

/// @name Global variables
/**@{*/

static struct timespec last_time = {};    ///< time of the log writing
static char* application_name = NULL;    ///< name of the application
static LoggerLevel init_level;            ///< level of the messages
static LoggerVerbosityLevel init_verb;    ///< verbosity of the messages
static char* init_fname = NULL;            ///< file name of the output file
static FILE* log_file = NULL;            ///< log-file

static uint32_t output_flags = 0;                ///< flags which determine the output
typedef void(*out_method)(LMessage* message);    ///< callback for the output method
static out_method* output_methods = NULL;        ///< pointer to the current output method

static pthread_mutex_t log_lock;        /// mutex which locks for writing

/**@}*/

/// @name Global functions
/**@{*/

static void to_syslog(LMessage* message);    ///< print the message to syslog
static void to_tty(LMessage* message);        ///< print the message to screen
static void to_file(LMessage* message);        ///< print the message to file

static char* getHeader(LoggerLevel log_level);                ///< get the header of the each message

static const int level2syslog(LoggerLevel log_level);        ///< translation the level to syslog type
static const char* level2str(LoggerLevel log_level);        ///< translation the level to string

/**@}*/
/**@}*/

/// @name Realization
/**@{*/

/// Initialization the logger
void lmsg_init(const char* app_name, LoggerLevel log_level, LoggerVerbosityLevel
               verbosity, uint32_t dev, char* file_name)
{
    init_level = log_level;
    init_verb = verbosity;
    output_flags = (dev & LOGGER_OUTPUT_MASK);
    init_fname = file_name;

    pthread_mutex_init(&log_lock, NULL);

    const char default_app_name[] = "unknown application";

    int sl = strlen(app_name);
    if(sl > MAX_APP_NAME_LEN - 1) sl = MAX_APP_NAME_LEN - 1;
    if(!sl)
    {
        sl = strlen(default_app_name);
        app_name = default_app_name;
    }

    application_name = malloc(sl);
    strcpy(application_name, app_name);

    if(init_fname) log_file = fopen(init_fname, "a");
    uint32_t tmp_m = output_flags;
    int c = 0;
    while(tmp_m)
    {
        if(tmp_m & 0x1)
            ++c;
        tmp_m = (tmp_m >> 1);
    }

    output_methods = (out_method*)malloc(c + 1);

    output_methods[c] = NULL;
    c = 0;
    if(output_flags & LOG_OUTPUT_SYSLOG)
    {
        openlog(application_name, 0, LOG_DAEMON);
        output_methods[c] = &to_syslog;
        ++c;
    }
    if(output_flags & LOG_OUTPUT_TTY)
    {
        output_methods[c] = &to_tty;
        ++c;
    }
    if(output_flags & LOG_OUTPUT_FILE)
    {
        output_methods[c] = &to_file;
        ++c;
    }
}

/// Print the message to an output device
void lmsg_log(LoggerLevel log_level, LoggerVerbosityLevel verbosity,
              const char* message, ...)
{
    if(init_level < log_level || init_verb < verbosity) return;

    static char buffer[4 * MAX_APP_NAME_LEN] = {};
    static LMessage msg_struct;

    pthread_mutex_lock(&log_lock);

        va_list args;
        va_start(args, message);
        vsprintf(buffer, message, args);
        va_end(args);

        msg_struct.level = log_level;
        msg_struct.header = getHeader(log_level);
        msg_struct.text = buffer;
        msg_struct.file = log_file;

        int i = -111111;
        for(i=0; output_methods[i]; ++i)
            output_methods[i](&msg_struct);

    pthread_mutex_unlock(&log_lock);
}

/// Print the message to syslog
static void to_syslog(LMessage* message)
{ syslog(level2syslog(message -> level), "%s", message -> text); }

/// Print the message to screen
static void to_tty(LMessage* message)
{
    printf("%s %s\n", message -> header, message -> text);
    fflush(stdout);
}

/// Print the message to file
static void to_file(LMessage* message)
{
    FILE* thf = message -> file;
    if(!thf) return;

    fputs(message -> header, thf);
    fputs(message -> text, thf);
    fputs("\n", thf);
    fflush(thf);
}

/// Get the header of the each message
static char* getHeader(LoggerLevel log_level)
{
    static char header_str[MAX_APP_NAME_LEN];
    static struct timespec rawtime = {};
    static struct tm* timeinfo = NULL;

    memset(header_str, 0, MAX_APP_NAME_LEN);
    if(clock_gettime(CLOCK_REALTIME, &rawtime) != -1)
    {
        timeinfo = localtime(&rawtime.tv_sec);
        static char tmp_t[21] = {}, tmp_t1[11] = {};
        if((!timeinfo -> tm_hour && !timeinfo -> tm_min && !timeinfo -> tm_sec) ||
           (!last_time.tv_sec && !last_time.tv_nsec))
        {
            strftime(tmp_t, sizeof(tmp_t), "%d.%m.%Y %A", timeinfo);
            strftime(tmp_t1, sizeof(tmp_t1), "%T", timeinfo);
            sprintf(header_str, "%s %s\n%s%s: ", tmp_t, application_name, level2str(log_level), tmp_t1);
        }
        else
        {
            strftime(tmp_t, sizeof(tmp_t), "%T", timeinfo);
            sprintf(header_str, "%s%s: ", level2str(log_level), tmp_t);
        }
        last_time = rawtime;
    }

    return header_str;
}

/// Translation the level to syslog type
static const int level2syslog(LoggerLevel log_level)
{
    static const int translation[] = { LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE,
                                        LOG_INFO, LOG_DEBUG };
    return translation[log_level];
}

/// Translation the level to string
static const char* level2str(LoggerLevel log_level)
{
    static const char* translation[] = { "EMERG  ", "ALERT  ", "CRIT  ", "ERROR  ", "WARNING  ",
                                            "NOTICE  ", "INFO  ",  "DEBUG  " };
    return translation[log_level];
}

/// Close the logger output and free memory
void lmsg_close()
{
    if(log_file)
        fclose(log_file);

    if(output_flags & LOG_OUTPUT_SYSLOG)
        closelog();

    if(output_methods)
        free(output_methods);

    free(application_name);
}

/**@}*/


Операционная система - QNX 6.5.0.
Компилятор - GCC 4.2.2.

Вот пример, на котором библиотека применялась:

main.cpp
#include <stdio.h>

#include "bit_operations.h"
#include "logger.h"

int main(int argc, char* argv[])
{
    char* fl_name = (char*)("/ram/logger_log.txt");
    lmsg_init("kc-logger", LogDebug, 11, LOG_OUTPUT_FILE, fl_name);
    printf("Welcome to the application kc-logger\n");

    int a = 1;
    printf("Before bit setting a = %i\n", a);
    int a_new = setBit4(a, 3);
    printf("After bit setting a = %i\n", a_new);

    lmsg_log(LogDebug, 9, "tttttt");
    lmsg_log(LogDebug, 9, "1111");
    lmsg_close();

    return 0;
}


bit_operations.h и bit_operations.cpp определены в этом тестовом примерчике:

bit_operations.h:
/** bit_operations.h
* This file describes the functions set/clear/check bit in byte
**/

#ifndef BIT_OPERATIONS_H_05550
#define BIT_OPERATIONS_H_05550

#include <stdbool.h>

/**
* @brief Sets a bit into 1 of the arg argument according to specified bit number num_bit
* @param arg - source argument
* @param num_bit - bit number
* @return New value of the argument arg
**/
unsigned char setBit1(unsigned char arg, unsigned int num_bit);
unsigned int setBit4(unsigned int arg, unsigned int num_bit);

/**
* @brief Sets a bit into 0 of the arg argument according to specified bit number num_bit
* @param arg - source argument
* @param num_bit - bit number
* @return New value of the argument arg
**/
unsigned char clearBit1(unsigned char arg, unsigned int num_bit);
unsigned int clearBit4(unsigned int arg, unsigned int num_bit);

/**
* @brief Checks a bit of the arg argument according to specified bit number num_bit
* @param arg - source argument
* @param num_bit - bit number
* @return True if the bit in argument arg is equal 1, false - otherwise
**/
bool checkBit1(unsigned char arg, unsigned int num_bit);
bool checkBit4(unsigned int arg, unsigned int num_bit);

/**
* @brief Extracts bits from bit number num_bit1 by bit number num_bit2
* @param arg - source argument
* @param num_bit1 - first bit number
* @param num_bit2 - second bit number
* @return Result of the extracting bits
**/
unsigned char extractBits1(unsigned char arg, unsigned int num_bit1, unsigned int num_bit2);
unsigned int extractBits4(unsigned int arg, unsigned int num_bit1, unsigned int num_bit2);

#endif // BIT_OPERATIONS_H_05550


bit_operations.cpp:
#include "bit_operations.h"

/// Sets a bit into 1 of the arg argument according to specified bit number num_bit
unsigned char setBit1(unsigned char arg, unsigned int num_bit)
{ arg |= (1 << num_bit); return arg; }
unsigned int setBit4(unsigned int arg, unsigned int num_bit)
{ arg |= (1L << num_bit); return arg; }

/// Sets a bit into 0 of the arg argument according to specified bit number num_bit
unsigned char clearBit1(unsigned char arg, unsigned int num_bit)
{ arg &= ~(1 << num_bit); return arg; }
unsigned int clearBit4(unsigned int arg, unsigned int num_bit)
{ arg &= ~(1L << num_bit); return arg; }

/// Checks a bit of the arg argument according to specified bit number num_bit
bool checkBit1(unsigned char arg, unsigned int num_bit)
{ return (arg & (1 << num_bit)); }
bool checkBit4(unsigned int arg, unsigned int num_bit)
{ return (arg & (1L << num_bit)); }

/// Extracts bits from bit number num_bit1 by bit number num_bit2
unsigned char extractBits1(unsigned char arg, unsigned int num_bit1, unsigned int num_bit2)
{ return (((arg & (((1 << (num_bit2 - num_bit1 + 1)) - 1) << num_bit1)) >> num_bit1)); }
unsigned int extractBits4(unsigned int arg, unsigned int num_bit1, unsigned int num_bit2)
{ return (((arg & (((1L << (num_bit2 - num_bit1 + 1)) - 1) << num_bit1)) >> num_bit1)); }
Iron Bug
1. убедись, что он точно находит все библиотеки и именно тех версий, которые тебе нужны (бывает так, что на машине каша из разных версий и линкер пытается прилинковать какую-нибудь древнюю версию, в которой таки действительно нет каких-то функций)
2. убедись, что правильно указан порядок линковки библиотек. он важен! иногда из-за неправильного указания порядка линковки линкер не может найти и слинковать все зависимости.
AD
Цитата(Iron Bug @ 21.3.2014, 10:45) *
1. убедись, что он точно находит все библиотеки и именно тех версий, которые тебе нужны (бывает так, что на машине каша из разных версий и линкер пытается прилинковать какую-нибудь древнюю версию, в которой таки действительно нет каких-то функций)
2. убедись, что правильно указан порядок линковки библиотек. он важен! иногда из-за неправильного указания порядка линковки линкер не может найти и слинковать все зависимости.

Ну такого рода проверки я уже делал. Повторюсь: если ту же самую библиотеку я соберу компилятором С++, то пример тут же будет работать.
Влад
Тогда проверяй, как манглятся имена экспортируемых/импортируемых функций. Мне доводилось сталкиваться с тем, что C-компилятор вставлял имя name, в то время как C++-компилятор вставлял (и, естессно, искал при импорте) для той же самой функции имя _name.
AD
Цитата(Влад @ 21.3.2014, 17:36) *
Тогда проверяй, как манглятся имена экспортируемых/импортируемых функций. Мне доводилось сталкиваться с тем, что C-компилятор вставлял имя name, в то время как C++-компилятор вставлял (и, естессно, искал при импорте) для той же самой функции имя _name.

А как это проверить, подскажешь? Mangle - искажаться... от этого слова слово манглятся????
Iron Bug
Цитата(Влад @ 21.3.2014, 19:36) *
Мне доводилось сталкиваться с тем, что C-компилятор вставлял имя name, в то время как C++-компилятор вставлял (и, естессно, искал при импорте) для той же самой функции имя _name.

обычно такое бывает только в венде, при сборке разными компиляторами. например, при попытках скрестить MinGW и MSVC.
для таких проблем существуют специальные утилиты, которые выкусывают это подчёркивание из библиотек. сейчас уже не помню, как называются, ибо давно уже не работаю с вендой.
ещё такая проблема возникает в MSVC, если битности библиотек не совпадают. подчёркивание - это 32-битный MSVC. 64-битные библиотеки не имеют подчёркивания в экспорте.

если же всё собирать одним компилятором и с одинаковой битностью, то проблем возникать не должно.
под никсами я с таким не сталкивалась, там все более-менее современные компиляторы совместимы по генерируемым именам функций.

возможно, С-библиотека импортирует какие-то функции. тогда они тоже должны вызываться с порядком передачи параметров типа "C". то есть, надо extern "C" ставить на всё, что она импортирует.


посмотрела внимательно код библиотеки.
там стоит
#ifdef __cplusplus
  extern "C" {
#endif

так вот, если ты это собираешь разными (C и C++) компияторами, то никакого ifdef там быть не должно. просто extern "C". иначе С++ компилятор это компилит по своим правилам и ищет не те функции.
AD
Цитата(Iron Bug @ 22.3.2014, 11:48) *
так вот, если ты это собираешь разными (C и C++) компияторами, то никакого ifdef там быть не должно. просто extern "C". иначе С++ компилятор это компилит по своим правилам и ищет не те функции.

Ну это вообще не компилябельно стало. Ошибки компиляции жуткие...


Короче, вопрос решился. То ли я забыл при пересборке библиотеки помимо самих библиотек в саму тестовую программку подсунуть новый хедерник, то ли еще что-то... но после полной пересборки и замены - все заработало. Т.е. для полного счастья строчек:
#ifdef __cplusplus
  extern "C" {
#endif
и в конце закрывающая скобка в заголовочном файле библиотеки вполне хватает! :)
Iron Bug
ну вот! о чём я, собственно, и говорила в самом начале темы. магии-то никакой нет :)
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Форум IP.Board © 2001-2024 IPS, Inc.