Правильно использование static функций

sadman41
Offline
Зарегистрирован: 19.10.2016

Раз уж новички сюда не пишут, то я побуду новичком.

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

Пытаюсь для этого применить static функции. Сразу предупрежу, что функциональность приведённого ниже кода отсутствует. Это материал, схожий по структуре с реальным кодом, и иллюстрирующий проблему:

static_test.ino

#include "t_static.h"

void setup() {
  Serial.begin(115200);
}

void loop() {
  int8_t rc;

  rc = getBMPMetric(0x76);
  Serial.println(rc);
  delay(1000);
}

t_static.h

#pragma once

int8_t getBMPMetric(const uint8_t);

t_static.cpp

#include <Arduino.h>
#include "t_static.h"

//#define READY_TESTING

static uint8_t waitToBMPReady(const uint8_t _i2cAddress) {
  //...
  return _i2cAddress*2;
}

int8_t getBMPMetric(const uint8_t _i2cAddress) {
  int8_t rc = true;
#if defined(READY_TESTING)
  rc = waitToBMPReady(_i2cAddress);
#endif
  return rc;
}

Если READY_TESTING определено, вызов waitToBMPReady() попадает в исходник и всё компилируется ОК.

Стоит READY_TESTING не определить (например - какой-то функционал в прошивке требуется отключить), вызов waitToBMPReady() в исходнике не присутсвует и компилятор обругивается: warning: 'waitToBMPReady' defined but not used. Оно, конечно, логично, но глаза мозолит.

Вопрос: как полагается делать правильно, чтобы компилятор не вываливал этот warning?

 

Green
Offline
Зарегистрирован: 01.10.2015

Ну так возьмите и waitToBMPReady() в #ifdef.

sadman41
Offline
Зарегистрирован: 19.10.2016

Я так и делаю - мой код прямо таки усеян ifdef-ами, поэтому я подумал, что делаю неправильно (ведь правильное должно быть красивое - не я же первый с таким вопросом, наверняка есть рецепт) и есть способ лучше.

mixail844
Offline
Зарегистрирован: 30.04.2012

а если так :  

#include <Arduino.h>
#include "t_static.h"

//#define READY_TESTING

static uint8_t waitToBMPReady(const uint8_t _i2cAddress) {
  //...
#ifdef (READY_TESTING)
  return _i2cAddress*2;
#else
  return 1;
#endif
}

int8_t getBMPMetric(const uint8_t _i2cAddress) {
  int8_t rc = true;
  rc = waitToBMPReady(_i2cAddress);
  return rc;
}

 

b707
Offline
Зарегистрирован: 26.05.2017

а если так:

t_static.cpp

#include <Arduino.h>
#include "t_static.h"

//#define READY_TESTING

static uint8_t waitToBMPReady(const uint8_t _i2cAddress)   __attribute__((used))
{
  //...
  return _i2cAddress*2;
}

int8_t getBMPMetric(const uint8_t _i2cAddress) {
  int8_t rc = true;
#if defined(READY_TESTING)
  rc = waitToBMPReady(_i2cAddress);
#endif
  return rc;
}

 

sadman41
Offline
Зарегистрирован: 19.10.2016

Так не компилируется, но если атрибут вперёд перенести, то компиляция проходит, предупреждений нет, однако... в полном соответствии с описанием "This attribute, attached to a function, means that code must be emitted for the function even if it appears that the function is not referenced." жирнит результирующий код на размер невызываемых, но существующих функций.

Однако, как оказалось, рядом (https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes) валяется аттрибут "unused": "This attribute, attached to a function, means that the function is meant to be possibly unused. GCC does not produce a warning for this function.". С ним, похоже, всё ОК.

__attribute__((__unused__))
static uint8_t waitToBMPReady(const uint8_t _i2cAddress) {
  //...
  return _i2cAddress * 2;
}

Но подожду ещё мнений - может изоляция делается проще, чем я тут наворачиваю.

b707
Offline
Зарегистрирован: 26.05.2017

sadman41 - ну ты же понял, что "я не настоящий сварщик", а этот ответ в StackOverflow нашел :) Так что извини.

sadman41
Offline
Зарегистрирован: 19.10.2016

Да не, я без претензий. По наводке понял, где покопать.

Кстати, оказывается gcc, за который IDE дёргает, понимает и такое:

  rc = getBMPMetric(0x76);
  switch (rc) {
    case 1 ... 7:
      Serial.println(rc);
      break;
    default:
      Serial.println("None");
      break;
  }

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Я не пользуюсь "unused" и любыми его "наколеночными" заменителями, а всегда честно исключаю дефайном.

То, что дефайнов много, так можно нужные функции компактно расположить (хоть в отдельном файле) и исключать чохом или сделать макрос для объявления функции и объявлять этим макросом (а дефайны в нём спрятаны).

Могу объяснить почему я не люблю "unused" и не пользуюсь отключением предупреждений в опциях компилятора. Есть несколько причин:

1. Уважаю предупреждения компилятора - пусть они работают как надо, иногда выручат. Не надо их затыкать.

2. Компилятор и/или линкер обычно выбрасывают из кода ненужные вещи, но это а) зависит от их опций и б) может не сработать в сложных случаях. А зачем мне в коде неиспользуемые функции? Лучше я их дефайном исключу.

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

sadman41 пишет:

Кстати, оказывается gcc, за который IDE дёргает, понимает и такое:

Я тоже только недавно узнал, что в чеснам С и так можно:

strcpy(to, from, count)
register char *to, *from;
register count;
{
    register n = (count + 7) / 8;
    if (!count) return;
    switch (count % 8) {
    case 0: do { *to++ = *from++;
    case 7:      *to++ = *from++;
    case 6:      *to++ = *from++;
    case 5:      *to++ = *from++;
    case 4:      *to++ = *from++;
    case 3:      *to++ = *from++;
    case 2:      *to++ = *from++;
    case 1:      *to++ = *from++;
               } while (--n > 0);
    }
}

 

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

всю башку себе сломал, пока разбирал чо тут к чему, так до конца и не понял, три дня пил потом. 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

DetSimen пишет:

Я тоже только недавно узнал, что в чеснам С и так можно:

Честный С - он такой :)

DetSimen пишет:

три дня пил потом. 

Это-то понятно, более интересно сколько ты пил ПЕРЕД тем, как такое написать :)

sadman41
Offline
Зарегистрирован: 19.10.2016

Кажется, что эта конструкция мне знакома - она где-то обсуждалась уже (что-то припоминаю про usenet) и, помнится, свела с ума не одного "прогера".

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

эт не я.  Это алгоритм копирования Даффа.  аналог   while(*dst++=*src++);   только не до нуля, а со счетчиком. 

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

sadman41 пишет:

Кажется, что эта конструкция мне знакома - она где-то обсуждалась уже (что-то припоминаю про usenet) и, помнится, свела с ума не одного "прогера".

Ну вот у меня до сих пор от этого сколиоз головного мозга.  И хочецца пить. 

Нету шансов у чесного Си против двух бутылок моего срецтва. 

sadman41
Offline
Зарегистрирован: 19.10.2016

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

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

DetSimen пишет:

Нету шансов у чесного Си против двух бутылок моего срецтва. 

Воистину!

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

sadman41 пишет:

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

Быстрее за счет развернутого цикла.

Остаток от степени двойки находится без команды деления.

Вообще - красивый код. Хотя и почти с GOTO.

Green
Offline
Зарегистрирован: 01.10.2015

DetSimen пишет:

Я тоже только недавно узнал, что в чеснам С и так можно:

strcpy(to, from, count)
register char *to, *from;
register count;
{
    register n = (count + 7) / 8;
    if (!count) return;
    switch (count % 8) {
    case 0: do { *to++ = *from++;
    case 7:      *to++ = *from++;
    case 6:      *to++ = *from++;
    case 5:      *to++ = *from++;
    case 4:      *to++ = *from++;
    case 3:      *to++ = *from++;
    case 2:      *to++ = *from++;
    case 1:      *to++ = *from++;
               } while (--n > 0);
    }
}


А мне иногда хотелось бы типа такой конструкции:
 

 REPT 128
*to++ = *from++;
ENDM