Estruturas de bibiliotecas em C duvidas [RESOLVIDO]

1. Estruturas de bibiliotecas em C duvidas [RESOLVIDO]

Lucas Fabian Marques Tomaz Silva
Lfabian

(usa Debian)

Enviado em 28/12/2022 - 18:00h

Olá, estudo linguagem C para microcontroladores, minha dúvida é algo que não nos ensinam normalmente em cursos, como estruturar corretamente uma biblioteca em C. No código abaixo criei um arquivo delay.h e lancei todo o código que preciso ali, não seria correto ter dois arquivos? (cabeçalho e classe) , ou a forma que fiz é aceita?
/*
* delay.h
*
* Created on: Dec 23, 2022
* Author: lfabian
*/
#ifndef INC_DELAY_H_
#define INC_DELAY_H_

#ifdef __cplusplus
extern "C" {
#endif

__STATIC_INLINE void delay_Init(void)
{
DWT->CYCCNT = 0;
CoreDebug->DEMCR = CoreDebug_DEMCR_TRCENA_Msk;
DWT->CTRL = DWT_CTRL_CYCEVTENA_Msk | DWT_CTRL_CYCCNTENA_Msk;

}
/**
* @brief This function provides a delay (in microseconds)
* @param microseconds: delay in microseconds
*/
__STATIC_INLINE void delay_us(volatile uint32_t microseconds)
{
uint32_t clk_cycle_start = DWT->CYCCNT;

/* Go to number of cycles for system */
microseconds *= (HAL_RCC_GetHCLKFreq() / 1000000);

/* Delay till end */
while ((DWT->CYCCNT - clk_cycle_start) < microseconds);
}
__STATIC_INLINE void delay_ms(volatile uint32_t milliseconds)
{
//for(uint32_t i= 0; i <milliseconds; i++) delay_us(1000);
HAL_Delay(milliseconds);
}

#ifdef __cplusplus
}
#endif

#endif



  


2. MELHOR RESPOSTA

Samuel Leonardo
SamL

(usa XUbuntu)

Enviado em 29/12/2022 - 09:24h


Lfabian escreveu:

Em meu projeto ficou assim, só para complementar O identificador extern não é compatível com static inline, se a classe de armazenamento for extern, o identificador terá ligação externa e a definição sequencial também fornecerá a definição externa.
Se a classe de armazenamento for static, o identificador possui ligação interna e a definição inline é invisível em outras unidades de tradução.
"A palavra-chave inline diz para o compilador substituir o código na definição de função para cada instância de uma chamada de função.
O uso das funções embutidas pode fazer com que seu programa seja mais rápido porque ele elimina a sobrecarga associada às chamadas de função. O compilador pode otimizar as funções embutidas expandidas de maneiras que não estão disponíveis para funções normais."
Pelo que entendi não posso usar inline quando uso extern correto? fiz o emprego de static inline para evitar desvios para pilha de processos que em microcontroladores tem um tamanho limitado geralmente, e pelo que entendo com static inline o código é implementado ao invés de ser chamada a função correto

Isso. Meu rammo não é bem com microcontroladores, mas vou tentar dar uma solução alternativa. No caso, vc poderia substituir as funções inline por simples macros (definições). É um pouco feio e fora de padrão mas ganha-se em velocidade porque todo o código fica "embutido" onde é colocada cada macro. O problema principal pode ser que, se vc "chamar" muitas vezes em locais diferentes as macros, o tamanho do executável pode ficar grande e assim consumir mais recursos de RAM no processo.

Exemplo:
//define a macro delay_Init
#define delay_Init {\
DWT->CYCCNT = 0;\
CoreDebug->DEMCR = CoreDebug_DEMCR_TRCENA_Msk;\
DWT->CTRL = DWT_CTRL_CYCEVTENA_Msk | DWT_CTRL_CYCCNTENA_Msk;\
}

Observe o '\' no final de cada linha.

Assim, vc pode chamar o delay_init dessa forma:
delay_Init; //e aqui irá substituir todo o código lá da definição

Repito, isso deixaria o código muito rápido mas em compensação vc estaria criando um executável maior. Funciona bem se vc tivesse poucas "chamadas" de macro em poucas partes de código. Num loop como while se vc tiver algo assim:
while (1) {
delay_init;//coloca todo o código aqui apenas uma vez
}

Acima, o código interno do delay será colocado ali só uma vez e então o while vai executar infinitamente com um unico delay_Init.
Agora, se vc for usar mais "chamadas" ao delay_Init, pode ser que aumente o tamanho do executável, pois, a cada inserção de delay_Init no código, é a mesma coisa que estar colocando todas as linhas do código de delay_Init. Vai a seu critério se vale a pena usar, porque se for poucas chamadas a cada função inline, pode ser que trocar por macros seja mais rápido: (mais ou menos como o inline)
Abaixo coloca duas vezes o mesmo código:
while (1) {
delay_init;//coloca todo o código aqui apenas uma vez
delay_init;//coloca todo o código mais uma vez, aumentando o tamanho do executável
}


com os dois arquivos não consegui compilar usando static inline, o compilador retorna undefined reference onde as funções de delay.h são chamadas. Estou um pouco confuso agora, não seria possível usar static inline com essa estrutura de arquivos?
fontes:https://learn.microsoft.com/pt-br/cpp/cpp/inline-functions-cpp?view=msvc-170 , https://learn.microsoft.com/pt-br/cpp/c-language/storage-class-specifiers-for-external-level-declara...

Pior que não pode usar static+inline com extern. Adiferença entre usar extern e static+inline é que extern diz que a declaração da função estará externamente num módulo (unidade de compilação), enquanto que static+inline diz que a definição e declaração estão todos num único arquivo na hora de compilar, o header no caso. Então isso pode confundir o compilador que fica esperando as duas coisas ao mesmo tempo. (Nem eu sabia disso mas olhei aqui agora e confirmei que é isso mesmo:
https://stackoverflow.com/questions/17504316/what-happens-with-an-extern-inline-function )

Como vc está trabalhando com variáveis globais, talvez seja interessante investir no que falei sobre usar macros (com parâmetros também), pra substituir as chamadas de função. Mas isso depende de como e quantas chamadas em pontos diferentes essas funções são usadas.

Veja como fica uma delas:
#define delay_ms(milliseconds){\
HAL_Delay(milliseconds);\
}

É quase a mesma coisa que o static+inline, porém é como falei, vai usar mais memória, pois obviamente estamos colocando repetições de código, mas ganha-se em velocidade.
A principal vantagem de declração extern é que é somente um único código de função pra tudo, diferente da solução alternativa que mostrei.


https://nerdki.blogspot.com/ acessa ai, é grátis
Não gostou? O ícone da casinha é serventia do site!

3. Re: Estruturas de bibiliotecas em C duvidas [RESOLVIDO]

Samuel Leonardo
SamL

(usa XUbuntu)

Enviado em 28/12/2022 - 20:38h

Isso também é correto, o porém que vejo seria caso vc tivesse uma classe que usasse outra classe e houvesse uma dependência cíclica entre elas.
Tipo, um header vc coloca a função delay, no outro ele usa a delay mas só que um terceiro usa o segundo header e dá conflito entre eles.

Pra evitar dependencia ciclica, basta apenas colocar em uma unidade de compilação, unidade de compilação é: header com a declaração + arquivo .c (ou .cpp) com a implementação das funções.

Assim, vc pode ter a função delay declarada no header delay.h e a implementação dela no arquivo delay.c e ai compilar com gcc -c delay.c, que vai gerar um arquivo chamado delay.o (código objeto). E pra compilar sem dependencia ciclica é só colocar o delay.o + header no arquivo do main: gcc -o main main.c delay.o


https://nerdki.blogspot.com/ acessa ai, é grátis
Não gostou? O ícone da casinha é serventia do site!


4. Re: Estruturas de bibiliotecas em C duvidas

Samuel Leonardo
SamL

(usa XUbuntu)

Enviado em 28/12/2022 - 20:44h

Exemplo:
Aqui abaixo é o header (delay.h) com as declarações:
/*
* delay.h
*
* Created on: Dec 23, 2022
* Author: lfabian
*/
#ifndef INC_DELAY_H_
#define INC_DELAY_H_

#ifdef __cplusplus
extern "C" {
#endif
/*
A palavra chave extern diz ao compilador que essas funções abaixo vão em um outro módulo (arquivo objeto)
*/
extern __STATIC_INLINE void delay_Init(void);
/**
* @brief This function provides a delay (in microseconds)
* @param microseconds: delay in microseconds
*/
extern __STATIC_INLINE void delay_us(volatile uint32_t microseconds);
extern __STATIC_INLINE void delay_ms(volatile uint32_t milliseconds);

#ifdef __cplusplus
}
#endif

#endif


e então vc cria outro arquivo, abaixo é o delay.c
#include "delay.h"

__STATIC_INLINE void delay_Init(void)
{
DWT->CYCCNT = 0;
CoreDebug->DEMCR = CoreDebug_DEMCR_TRCENA_Msk;
DWT->CTRL = DWT_CTRL_CYCEVTENA_Msk | DWT_CTRL_CYCCNTENA_Msk;

}
/**
* @brief This function provides a delay (in microseconds)
* @param microseconds: delay in microseconds
*/
__STATIC_INLINE void delay_us(volatile uint32_t microseconds)
{
uint32_t clk_cycle_start = DWT->CYCCNT;

/* Go to number of cycles for system */
microseconds *= (HAL_RCC_GetHCLKFreq() / 1000000);

/* Delay till end */
while ((DWT->CYCCNT - clk_cycle_start) < microseconds);
}
__STATIC_INLINE void delay_ms(volatile uint32_t milliseconds)
{
//for(uint32_t i= 0; i <milliseconds; i++) delay_us(1000);
HAL_Delay(milliseconds);
}

Dai vc compila com:
gcc -c delay.c
E linka com:
//main.c é o arquivo onde tem o main()
//esse main.c usa include "delay.h"
gcc -o main main.c delay.o

https://nerdki.blogspot.com/ acessa ai, é grátis
Não gostou? O ícone da casinha é serventia do site!


5. Re: Estruturas de bibiliotecas em C duvidas [RESOLVIDO]

Lucas Fabian Marques Tomaz Silva
Lfabian

(usa Debian)

Enviado em 28/12/2022 - 20:56h


SamL escreveu:

Isso também é correto, o porém que vejo seria caso vc tivesse uma classe que usasse outra classe e houvesse uma dependência cíclica entre elas.
Tipo, um header vc coloca a função delay, no outro ele usa a delay mas só que um terceiro usa o segundo header e dá conflito entre eles.

Pra evitar dependencia ciclica, basta apenas colocar em uma unidade de compilação, unidade de compilação é: header com a declaração + arquivo .c (ou .cpp) com a implementação das funções.

Assim, vc pode ter a função delay declarada no header delay.h e a implementação dela no arquivo delay.c e ai compilar com gcc -c delay.c, que vai gerar um arquivo chamado delay.o (código objeto). E pra compilar sem dependencia ciclica é só colocar o delay.o + header no arquivo do main: gcc -o main main.c delay.o


https://nerdki.blogspot.com/ acessa ai, é grátis
Não gostou? O ícone da casinha é serventia do site!

Vamos ver se entendi, no meu exemplo se eu empregasse delay.h em um headset ex: display.h que faz uso das funções de delay.h e no meu main eu fizesse uso de delay.h e display.h o compilador retornaria erro de múltiplas dependências correto?



6. Re: Estruturas de bibiliotecas em C duvidas

Samuel Leonardo
SamL

(usa XUbuntu)

Enviado em 28/12/2022 - 22:07h

Lfabian escreveu:

Vamos ver se entendi, no meu exemplo se eu empregasse delay.h em um headset ex: display.h que faz uso das funções de delay.h e no meu main eu fizesse uso de delay.h e display.h o compilador retornaria erro de múltiplas dependências correto?

Não exatamente assim, porque seu delay.h tem include guards. Depenência circular é mais comum com declaração de structs onde uma inclui a outra que não foi declarada ainda.
Veja um exemplo de código aqui:
https://stackoverflow.com/questions/10122621/c-circular-dependency

Tem como fazer a declaração da struct sem especificar os dados dela, mas se vc tiver apenas um header sem um módulo (arquivo .c) e uma das funçlões acessar um dado da struct vai bugar tudo, porque como só existe a delcração mas sem especificação dos dados, o compilador não vai saber o que fazer. É por isso que eu pessoalmente prefiro declarar em unidades arquivo.h+arquivo.c pra evitar tal erro na hora de programar, mesmo sabendo que exista a delcaração "a frente" como opção.


https://nerdki.blogspot.com/ acessa ai, é grátis
Não gostou? O ícone da casinha é serventia do site!


7. Re: Estruturas de bibiliotecas em C duvidas

Lucas Fabian Marques Tomaz Silva
Lfabian

(usa Debian)

Enviado em 29/12/2022 - 08:49h

Em meu projeto ficou assim, só para complementar O identificador extern não é compatível com static inline, se a classe de armazenamento for extern, o identificador terá ligação externa e a definição sequencial também fornecerá a definição externa.
Se a classe de armazenamento for static, o identificador possui ligação interna e a definição inline é invisível em outras unidades de tradução.
"A palavra-chave inline diz para o compilador substituir o código na definição de função para cada instância de uma chamada de função.
O uso das funções embutidas pode fazer com que seu programa seja mais rápido porque ele elimina a sobrecarga associada às chamadas de função. O compilador pode otimizar as funções embutidas expandidas de maneiras que não estão disponíveis para funções normais."
Pelo que entendi não posso usar inline quando uso extern correto? fiz o emprego de static inline para evitar desvios para pilha de processos que em microcontroladores tem um tamanho limitado geralmente, e pelo que entendo com static inline o código é implementado ao invés de ser chamada a função correto?
com os dois arquivos não consegui compilar usando static inline, o compilador retorna undefined reference onde as funções de delay.h são chamadas. Estou um pouco confuso agora, não seria possível usar static inline com essa estrutura de arquivos?
fontes: https://learn.microsoft.com/pt-br/cpp/cpp/inline-functions-cpp?view=msvc-170 , https://learn.microsoft.com/pt-br/cpp/c-language/storage-class-specifiers-for-external-level-declara...
usando extern o código ficou assim, foi preciso referenciar as variáveis do microcontrolador no arquivo cabeçalho
/*
* delay.h
*
* Created on: Dec 23, 2022
* Author: lfabian
*/
#ifndef DELAY_H_
#define DELAY_H_

#include "stdint.h"
#include "stm32f4xx_hal.h"

#ifdef __cplusplus
extern "C" {
#endif

extern void delay_Init(void);

extern void delay_us(volatile uint32_t microseconds);

extern void delay_ms(volatile uint32_t milliseconds);

#ifdef __cplusplus
}
#endif

#endif

/*
* delay.c
*
* Created on: 29 de dez de 2022
* Author: lfabian
*/

#include "delay.h"

/*
* @brief This function provides an initialization of the cycle counter in DWT
* @param None
*/
void delay_Init(void)
{
DWT->CYCCNT = 0;
CoreDebug->DEMCR = CoreDebug_DEMCR_TRCENA_Msk;
DWT->CTRL = DWT_CTRL_CYCEVTENA_Msk | DWT_CTRL_CYCCNTENA_Msk;

}
/**
* @brief This function provides a delay_us(in microseconds)
* @param microseconds: delay in microseconds
*/
void delay_us(volatile uint32_t microseconds)
{
uint32_t clk_cycle_start = DWT->CYCCNT;

/* Go to number of cycles for system */
microseconds *= (HAL_RCC_GetHCLKFreq() / 1000000);

/* Delay till end */
while ((DWT->CYCCNT - clk_cycle_start) < microseconds);
}
/**
* @brief This function provides a delay_ms(in milliseconds)
* @param milliseconds: delay in milliseconds
*/
void delay_ms(volatile uint32_t milliseconds)
{
//for(uint32_t i= 0; i <milliseconds; i++) delay_us(1000);
HAL_Delay(milliseconds);
}



Edit:
realizei alguns testes para tentar confirmar se meu raciocínio está correto, com o primeiro arquivo usando static inline e com a estrutura em 2 arquivos (que acho esteticamente melhor) usando exern as saidas do compilador ficaram:

text data bss dec hex filename
21612 260 10116 31988 7cf4 test_extern.elf
text data bss dec hex filename
21616 260 10116 31992 7cf8 test_inline.elf

onde text é o código compilado que está ligeiramente maior usando static inline, enquanto data (variáveis inicializadas) e bss(variáveis não-inicializadas) ficaram inalterados, para verificar se ouve mudança no tamanho da pilha de processos falta realizar os testes em debug


8. Re: Estruturas de bibiliotecas em C duvidas [RESOLVIDO]

Lucas Fabian Marques Tomaz Silva
Lfabian

(usa Debian)

Enviado em 29/12/2022 - 10:16h

Interessante não sabia da possibilidade de macros, mas acredito que no fundo seja isso que está ocorrendo já que faço uso de uma palavra-chave específica
__STATIC_INLINE 
aprendi que se deve usar assim invés de
static inline 
e nunca me questionei o porque afinal das duas formas compila, mas acredito que o compilador gera macro quando usa o primeiro, realmente programar para microcontroladores cria muitos dilemas pela escassez de recursos. agradeço pelos esclarecimentos.



9. Re: Estruturas de bibiliotecas em C duvidas [RESOLVIDO]

Samuel Leonardo
SamL

(usa XUbuntu)

Enviado em 30/12/2022 - 00:13h


Lfabian escreveu:

Interessante não sabia da possibilidade de macros, mas acredito que no fundo seja isso que está ocorrendo já que faço uso de uma palavra-chave específica
__STATIC_INLINE 
aprendi que se deve usar assim invés de
static inline 
e nunca me questionei o porque afinal das duas formas compila, mas acredito que o compilador gera macro quando usa o primeiro, realmente programar para microcontroladores cria muitos dilemas pela escassez de recursos. agradeço pelos esclarecimentos.

Eu diria que o __STATIC_INLINE seja uma definição de um compilador específico enquanto que o "static inline" é uma definição padrão da linguagem.
De qualquer forma, creio a diferença dependa apenas do compilador usado e da versão do C que vc tá usando.


https://nerdki.blogspot.com/ acessa ai, é grátis
Não gostou? O ícone da casinha é serventia do site!


10. Re: Estruturas de bibiliotecas em C duvidas [RESOLVIDO]

Paulo
paulo1205

(usa Ubuntu)

Enviado em 31/12/2022 - 15:44h

Normalmente você divide os arquivos da seguinte forma:

    • a interface, que é o que o compilador precisa conhecer para que o programador possa usar a sua biblioteca sem produzir erros de compilação, vai no(s) arquivo(s) de cabeçalhos (tipicamente com sufixo “.h”), que permanece(m) em formato de código fonte e é (são) lido(s) a cada nova compilação de código que use sua biblioteca;

    • a implementação, que é o que o linker precisa conhecer para poder viabilizar a execução daquilo que a sua biblioteca se propõe a fazer, geralmente reside em um ou mais arquivos já compilados, quer como arquivos avulsos com código objeto (sufixo “.o” ou “.po” no mundo Unix, ou “.OBJ” no mundo Microsoft) quer em arquivos de bibliotecas estáticas (sufixo “.a” ou “.LIB”) ou dinâmicas (sufixo “.so” ou “.DLL”).

Um arquivo de cabeçalhos em C geralmente não provoca emissão de código objeto, porque contém apenas declarações de objetos (variáveis e funções) e de tipos incompletos, e definições de tipos (structs, unions, typedefs, enums), macros (i.e. #defines) e constantes na forma de labels de enumerações.

Em C++, cabeçalhos podem conter também outras definições de tipo (classes, enum class, enum struct, using), templates (mas não seu instanciamento), concepts, e muitas pessoas admitem também definições de constantes de tipos nativos (por exemplo: “const int NUMBER=5;”) e funções inline (explicitamente ou implicitamente, quando possível) porque, embora estas duas últimas possam provocar emissão de código, em C++ tais definições de constantes têm ligação interna, ao contrário do C, em que teriam ligação externa. No caso de templates que possuam código C++ mais extenso, é comum (o GCC, por exemplo, faz isso) separar a parte mais declarativa, que permanece no arquivo com sufixo “.h”, da parte que contém os algoritmos dos templates de funções ou funções-membros de classes, que é movida para arquivos com sufixo “.tcc”, fazendo com que o arquivo “.h” tenha um #include do arquivo “.tcc”. Note, porém, que o arquivo “.tcc” não provoca imediatamente emissão de código objeto no momento da compilação, pois ele contém apenas a definição dos algoritmos de templates; a emissão de código correspondente somente ocorre quando o template é instanciado.

A implementação daquilo que é descrito nos arquivos de cabeçalho geralmente reside em um ou mais arquivos com sufixos “.c” (para linguagem C) ou “.C”, “.cc”, “.cpp” ou “.cxx” (para C++), e incluem a definição de objetos (variáveis e funções), a definição de tipos incompletos e o possível instanciamento de templates. Tais arquivos fontes são então compilados para o formato de código objeto, como acima mencionado, e são distribuídos diretamente nessa forma, ou, principalmente quando há múltiplos arquivos com código objeto, podem ser aglutinados na forma de bibliotecas estáticas e/ou dinâmicas (apenas um arquivo para cada versão).


Para exemplificar essa divisão, seguem alguns trechos de uma biblioteca que eu fiz para extração de dados a partir de uma fonte na rede (não posso publicar na íntegra por enquanto), ilustrando a divisão em “.h”, “.tcc” e “.cc”.

// Arquivo my_lib.h

#ifndef MY_LIB_H_included
#define MY_LIB_H_included


#include <string>
#include <stdexcept>


// Definição de tipo (classe) incluindo definição função-membro implicitamente inline (i.e. é inline mesmo sem ter a palavra-chave inline na declaração).
class my_exception: public std::runtime_error {
private:
int m_errcode;

public:
my_exception(int errcode);
my_exception(int errcode, const std::string &extra_info);

int errcode() const { return m_errcode; } // inline implícito (OK no cabeçalho).
};

/* ... */

// Algumas definições de tipos, incluindo alguns tipos com templates.

struct table_parse_error {
std::string raw_key, raw_data, message;

table_parse_error(const std::string &k, const std::string &d, const std::string &m);
bool operator <(const map_parse_error &other) const;
};

using parse_error_set=std::set<table_parse_error>; // “using” é uma alternativa a “typedef”.


// Tipos templated auxiliares para identificar se a extração está sendo feita para um container do tipo chave+valor (tal como std::map, std::unordered_map) ou que contém apenas chaves (tal como std::set),
// e também se a chave pode ocorrer apenas uma vez (tal como std::set, std::map, std::unordered_map) ou também chaves duplicadas (std::multiset, std::multimap etc.).

// Template de classe genérica para seleção do tipo correto de container (o caso geral supõe container chave+valor sem chaves duplicadas).
template <class CONT, bool IS_KEY_ONLY, bool IS_MULTI> struct tp_helper {
using K=typename CONT::key_type;
using mapped_type=typename CONT::mapped_type;
static bool default_inserter(CONT &st, const K &key, const mapped_type &val);
};

// Especialização parcial de tp_helper para containers chave+valor com duplicatas (e.g. std::multimap, std::unordered_multimap).
template <class CONT> struct tp_helper<CONT, false, true> {
using K=typename CONT::key_type;
using mapped_type=typename CONT::mapped_type;
static bool default_inserter(CONT &st, const K &key, const mapped_type &val);
};

// Especialização parcial de tp_helper para containers somente chave sem duplicatas (e.g. std::set, std::unordered_set).
template <class CONT> struct tp_helper<CONT, true, false> {
using K=typename CONT::key_type;
using mapped_type=std::string;
static bool default_inserter(CONT &st, const K &key, const mapped_type &val);
};

// Especialização parcial de tp_helper para containers somente chave com duplicatas (e.g. std::multiset).
template <class CONT> struct tp_helper<CONT, true, true> {
using K=typename CONT::key_type;
using mapped_type=std::string;
static bool default_inserter(CONT &st, const K &key, const mapped_type &val);
};

// Tipo para identificar se o container é somente valor: compara o tipo da chave com o tipo do valor armazenado.
template <class CONT> struct is_key_only {
static constexpr bool value=std::is_same<typename CONT::key_type, typename CONT::value_type>::value;
};

// Tipo para identificar se o container permite chaves duplicadas: vê se o tipo de retorno da função de inserção de valor é um iterador do container.
template <class CONT> struct is_multi_key {
static constexpr bool value=
std::is_same<
typename CONT::iterator,
decltype(CONT().insert(std::declval<typename CONT::value_type>()))
>::value
;
};


// Tipo para receber dados da tabela e popular um container.
template <class CONT> class table_parser {
public:
using K=typename CONT::key_type; // Tipo da chave.
using CONT_info=tp_helper<CONT, is_key_only<CONT>::value, is_multi_key<CONT>::value>; // Tipo informativo sobre o container (alias para uma instância de tp_helper para este container).
using V=typename CONT_info::mapped_type; // Tipo do valor (se for um container que só tem chave, usa std::string como dummy (ver acima), pois é o mesmo tipo usado durante o parsing da tabela).

protected:
S *p_storage;
/* ... */

public:
table_parser(CONT &storage);
/* ... */

/* etc. */
};


// Declaração de alguns templates de funções.
template<class CONT> void gettable(const table_parser<CONT> &extractor, const std::string &database, const std::string &table);
template<class K=std::string, class V=std::string> std::map<K, V> gettable(const std::string &database, const std::string &table);
template<class K=std::string> std::set<K> getkeys(const std::string &database, const std::string &table);

// Declaração de função comum.
std::string getvalue(const std::string &database, const std::string &nismap, const std::string &key);


// Note a inclusão do arquivo my_lib.tcc aqui. A separação foi feita para não poluir visualmente o arquivo my_lib.h,
// permitindo a quem o lê enxergue as declarações, sem se perder com os algoritmos convolutos dos templates.
#include "my_lib.tcc"

#endif // !defined(MY_LIB_H_included)

// Arquivo my_lib.tcc

// Algoritmo genérico (templated) para inseridor padrão não-especializado (containers chave+valor sem chaves duplicadas).
template <class CONT, bool IS_KEY_ONLY, bool IS_MULTI> bool tp_helper<CONT, IS_KEY_ONLY, IS_MULTI>::default_inserter(CONT &s, const K &k, const mapped_type &v){
auto result=s.emplace(k, v);
if(!result.second){
if(result.first==s.end())
return false;
result.first->second=v;
}
return true;
}

// Algoritmo genérico (templated) para inseridor padrão especializado para containers chave+valor com chaves duplicadas.
template <class CONT> bool tp_helper<CONT, false, true>::default_inserter(CONT &s, const K &k, const mapped_type &v){
auto result=s.emplace(k, v);
return result!=s.end();
}

// Algoritmo genérico (templated) para inseridor padrão especializado para containers somente-chave sem chaves duplicadas.
template <class S> bool tp_helper<S, true, false>::default_inserter(S &s, const K &k, const mapped_type &){
auto result=s.emplace(k);
return result.second || result.first!=s.end();
}

// Algoritmo genérico (templated) para inseridor padrão especializado para containers somente-chave com chaves duplicadas.
template <class S> bool tp_helper<S, true, true>::default_inserter(S &s, const K &k, const mapped_type &){
auto result=s.emplace(k);
return result!=s.end();
}


template<class CONT> void gettable(const table_parser<CONT> &extractor, const std::string &database, const std::string &table){
/* ... */
}

template<class K=std::string, class V=std::string> std::map<K, V> gettable(const std::string &database, const std::string &table){
using CONT=std::map<K, V>;
CONT result;
table_parser<CONT>(result);
/* ... */
return result;
}

template<class K=std::string> std::set<K> getkeys(const std::string &database, const std::string &table){
using CONT=std::set<K>;
CONT result;
table_parser<CONT>(result);
/* ... */
return result;
}


// Arquivo my_lib.cc

#include <string>

#include <cstring>

#include "my_lib.h" // Pega as declarações necessárias.


using namespace std;


// Implementação de funções-membos não-templated.
my_exception::my_exception(int errcode):
runtime_error(strerror(errcode)), m_errcode(errcode)
{
}

my_exception::my_exception(int errcode, const string &extra_info):
runtime_error(
((string(strerror(errcode))+=" [")+=extra_info)+=']'
), m_errcode(errcode)
{
}


/* ... */


string getvalue(const string &database, const string &nismap, const string &key){
char *db_data=nullptr;
size_t db_data_size=0;
/* ... */
if(!db_data)
throw my_exception(errno);
return string(db_data, db_data_size);
}


No Linux, a biblioteca estática se produz a partir o arquivo objeto.
g++  -Wall -Werror -pedantic-errors -O2 -std=c++11  -c my_lib.cc
ar rv libmy_lib.a my_lib.o
ranlib libmy_lib.a


Já a biblioteca dinânima pode ser gerada a partir de um objeto compilado com opção de gerar código relocável (position independent code).
g++  -Wall -Werror -pedantic-errors -O2 -std=c++11  -fPIC -c my_lib.cc -o my_lib.lo
g++ -shared -fPIC -o libmy_lib.so my_lib.lo



Para usar num programa, o código fonte em C++ precisa incluir o cabeçalho.
#include "my_lib.h" 


E o executável gerado tem de fazer referência a uma das bibliotecas acima (por exemplo, usando a biblioteca estática).
g++ -Wall -Werror -pedantic-errors -O2 -std=c++11 -o test test.cc libmy_lib.a 



... Então Jesus afirmou de novo: “(...) eu vim para que tenham vida, e a tenham plenamente.” (João 10:7-10)






Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts