LINUX.ORG.RU

А как правильно делать setter в С?

 ,


0

1

Чот дико туплю но не могу найти ответа на вопрос как сделать простенький setter - т.е. задать значение переменной через функцию, которая принимает аргументом переменную с таким же названием:

uint8_t verboose;

void setVerboose(uint8_t verboose) {
  this.verboose = verboose;
}

понятно что можно:

uint8_t verboose;

void setVerboose(uint8_t verbooseExt) {
  verboose = verbooseExt;
}

но хотелось бы так как хотелось бы
или в С так никак?

★★★★

Я бы сделал именно вариант с verbooseExt и не парился бы вообще

А то потом в рандомном куске кода возникнет вопрос а это какой verboose, Ext или глобальный?

nikitalol
()

Тут вопрос не в сеттере, а в том, кто отвечает за управление памятью.

Если вызывающий, то надо передавать два параметра: указатель на переменную, которую задаём (может быть своего типа, либо uint8_t как у Вас) и само новое значение. Если вызываемый, то можно конечно держать переменную в статической памяти, но хотя бы ограничить облать видимости одной единицей трансляции чтобы никто кроме setVerboose() не смог менять + держать в голове конкурентный доступ.

dsl
()

задать значение переменной через функцию, которая принимает аргументом переменную с таким же названием

если читать пожелание дословно, то можно так

#define setSpeed(name,value) do { \
   if (0<=(value) && ((typeof value)299792458)>(value)) { \
       (name) = (value);    \
   }  \
}while(0)

то есть в «макрос» передали имя и значение, внутри проверили допустимость, присвоили заданному имени

или можно извратиться в точности как вы хотите

#include <stdio.h>
int verbose=0;
#define verbose(x) verbose=(x)
int main()
{
	verbose(1);
	printf("verbose=%d",verbose);
	return 0;
}
но такое чревато, в том числе синяками от сотоварищей :-)

MKuznetsov ★★★★★
()
Ответ на: комментарий от andalevor

Сеттеры-геттеры это разве не про ООП? Может что-то вроде:

Бинго, структуры! Спасибо!

Я прост в С очень тугой но в явушке привык заранее обернуть переменные геттерами/сеттерами на случай если потом придется к переменным привязывать логику - это, конечно, можно и без наследования имен делать но категорически удобно же когда переменная (в чуток измененном сознании, да :D) может сразу и дернуть что-то и самопровериться:

struct {
  uint8_t channel;
  uint8_t longrange;
  uint8_t verboose;
} settings = {
    .channel = 0,
    .longrange = 1,
    .verboose = 0
};


void setVerboose (uint8_t verboose) {
// Например, включаем порт для отладки
  settings.verboose = verboose; // И дальше локально в этой либе можем надеяться что все что надо - уже сделано если просто проверить settings.verboose
}

А можно при нужде сразу расширить функционал:

void setVerboose (uint8_t verboose, uint8_t port) {
// Например, включаем *конкретный* порт для отладки если у нас вдруг появилась железяка в которой отладочный порт не стандартный
  settings.verboose = verboose;
}

И тогда нам не надо переписывать все вызовы setVerboose() а достаточно добавить «переадресацию» с портом по умолчанию, на который надеятся все старые вызовы:

void setVerboose (uint8_t verboose) {
  setVerboose(verboose, DEFAULT_PORT);
}

Либо можно его вообще не хранить как переменную и постоянно дергать геттер, который проверит нужные условия:

uint8_t getVerboose() {
  if (например, проверяем включен ли отладочный порт){
    return 1;
  } else if (а может включена локальная запись отладки) {
    return 1;
  } else {
    return 0;
  } 
}

Ну и можно в геттере, если он дорогой, обновлять напрямки settings.verboose и локально геттер дергать только по нужде а в остальное время пользовать переменную

т.е. если долго курить яву то привыкаешь к тому что переменная обычно это сразу три-в-одном и это, на самом деле, часто - категорически удобно (особливо для библиотек, которые дергаются из кучи мест) ибо если оставлять только функции то начинается разброд и шатания с «вот эта функция в принципе дорогая» или «эту функцию нельзя выполнять два раза» или "два раза можно но в два раза дольше что в итоге приводит к аналогичной концепции но без радости от общего стройного порядка вещей :D

rukez ★★★★
() автор топика
Ответ на: комментарий от rukez

в явушке привык заранее обернуть переменные геттерами/сеттерами

Ты же понимаешь, что не нужно тащить концепции из джавы с ООП сахарком в анскильную процедурную сишечку? Ничего хорошего из этого не выйдет.

ox55ff ★★★★★
()
Ответ на: комментарий от ox55ff

Ты же понимаешь, что не нужно тащить концепции из джавы с ООП сахарком в анскильную процедурную сишечку? Ничего хорошего из этого не выйдет

так эта концепция не прибита гвоздями к ООП - ты просто привязываешь (мысленно) к переменной две процедуры - записи и чтения значения, и дальше по нужде дергаешь их вместо обычной записи/чтения прямо в переменную - не уверен что компилятор си это сможет нормально оптимизировать и соотв прям совсем без разбора оно может и не надо, но в ряде случаев - прям очень удобно

ну и бонусом - из .h получается отличный класс со статическими полями - т.е. вполне себе ООП когда нет нужды плодить сами объекты (да, это не канонично но вот как то так)

В си имя функции соответствует имени символа, по этому ты не можешь объявить несколько функций с одинаковым именем и разной сигнатурой

пле
зря зря (с) :D
вариант с макросами нагуглил но не понравилось
штош, попробуем жить без перегрузок

rukez ★★★★
() автор топика
Ответ на: комментарий от mittorn

В си имя функции соответствует имени символа, по этому ты не можешь объявить несколько функций с одинаковым именем и разной сигнатурой

Это обходится макрокостылем с _Generic

SZT ★★★★★
()
Ответ на: комментарий от rukez

Ну если хочешь, то можешь руками заимплементировать виртуальные таблицы. Что-то типа:

#include<stdio.h>

typedef struct {
    void(*say)();
} Animal;

static void SayCat() {
    printf("Meow");
}

static void SayDog() {
    printf("Wof");
}

Animal newCat() {
    Animal animal;
    animal.say = &SayCat;
    return animal;
}

Animal newDog() {
    Animal animal;
    animal.say = &SayDog;
    return animal;
}

int main() {
    Animal animal = newDog();
    animal.say();
    return 0;
}

И будут тебе перегрузки. Но тут есть проблемы:

  1. Это дополнительные простыни кода на каждый «класс»
  2. Джававского наследования все равно не сделаешь, придется решать задачи через аппликацию
  3. Ты добавляешь дополнительные операции и дополнительно тратишь память
Aswed ★★★★★
()
Ответ на: комментарий от Aswed

Спасибо! Вчера нашел это, вечером не зашло, с утра на свежую голову переосмыслил - в целом получается весьма недурное наследование, притом местами даже лаконичней чем в яве

Вопрос можно ли реализовать аналог abstract для ссылки на функцию? Т.е. что бы каждый экземпляр структуры гарантировано определял функцию для ссылки (кроме очевидного варианта со ссылкой по умолчанию на некую ругающуюся на неопределенность функцию)?
И если нет, то можно как то реализовать error компилятору из собсно функции т.е. что бы ошибка компиляции стреляла только при условии что функция реально вызывается где то в коде а не просто задекларирована, передана ссылкой но потом заменена на другую функцию (экий я лихой костыле-генератор :D)

rukez ★★★★
() автор топика
Ответ на: комментарий от rukez

Я не пишу на яве и не знаю, что там такое abstract. Но сделать указанную проверку компилятором конечно же не выйдет, потому что подобная сборка «виртуальных таблиц» происходит в рантайме, а не во время компиляции. И вообще заниматься переопределением переопределений не рекомендую, поскольку на дебаге ты вообще не поймешь что и откуда вызывается.

Aswed ★★★★★
()
Ответ на: комментарий от Aswed

abstract обязывает каждого наследника реализовать отмеченную функцию, проверяется как раз на этапе компиляции т.е. хотелось бы вот такого:

#include<stdio.h>

typedef struct {
    abstract void(*say)(); // условно - пометка компилятору что структура обязана содержать ссылку в момент инициализации
} Animal;

static void SayCat() {
    printf("Meow");
}

static void SayDog() {
    printf("Wof");
}

Animal newCat() {
    Animal animal = {
    animal.say = &SayCat} ; // нет ошибки, ссылка гарантировано заполнена
    return animal;
}

Animal newDog() {
    Animal animal; // ошибка компилятора - создана структура без гарантии заполнения ссылки
    animal.say = &SayDog;
    return animal;
}
rukez ★★★★
() автор топика
Ответ на: комментарий от rukez

Можно объявить указатель на функцию константным:

#include <stdio.h>

typedef struct {
	void (*const say)(void);
} Animal;

static void SayCat(void) {
	printf("Meow");
}

static void SayDog(void) {
	printf("Wof");
}

static Animal newCat(void) {
	Animal animal = {
		.say = SayCat
	}; // нет ошибки, ссылка гарантировано заполнена
	return animal;
}

static Animal newDog(void) {
	Animal animal;
	// gcc: assignment of read-only member ‘say’
	// clang: cannot assign to non-static data member 'say' with const-qualified type 'void (*const)(void)'
	animal.say = SayDog;
	return animal;
}
goto-vlad
()
Последнее исправление: goto-vlad (всего исправлений: 1)
Ответ на: комментарий от leave

Заметь, ты пришёл в development. По сути ничего не сказал, но начал скатывать тему в «кококо».

p.s. ты ещё слово «очепятка» начни поправлять, существо без чувства языка.

fluorite ★★★★★
()
MODULE Figures; (* Abstract module *)

TYPE
   Figure*    = POINTER TO FigureDesc;
   Interface* = POINTER TO InterfaceDesc;

   InterfaceDesc* = RECORD
      draw*  : PROCEDURE (f : Figure);
      clear* : PROCEDURE (f : Figure);
      mark*  : PROCEDURE (f : Figure);
      move*  : PROCEDURE (f : Figure; dx, dy : INTEGER);
   END;

   FigureDesc* = RECORD
      if : Interface;
   END;

PROCEDURE Init* (f : Figure; if : Interface);
BEGIN
   f.if := if
END Init;

PROCEDURE Draw* (f : Figure);
BEGIN
   f.if.draw(f)
END Draw;

(* Other procedures here *)

END Figures.

MODULE Rectangles;

IMPORT Figures;

TYPE
   Rectangle* = POINTER TO RectangleDesc;

   RectangleDesc* = RECORD
      (Figures.FigureDesc)
      x, y, w, h : INTEGER;
   END;

VAR
   if : Figures.Interface;

PROCEDURE New* (VAR r : Rectangle);
BEGIN
   NEW(r);
   Figures.Init(r, if)
END New;

PROCEDURE Draw* (f : Figure);
   VAR
      r : Rectangle;
BEGIN
   r := f(Rectangle); (* f AS Rectangle *)
   (* ... *)
END Draw;

(* Other procedures here *)

BEGIN (* Module initialisation *)
   NEW(if);
   if.draw  := Draw;
   if.clear := Clear;
   if.mark  := Mark;
   if.move  := Move
END Rectangles.

Еще пример на Обероне.

LongLiveUbuntu ★★★★★
()
Ответ на: комментарий от fluorite

А я думал ТС намеренно использует «verboose». Что-то вроде «verbose» (многословный) + «booze» (бухло, выпивка). В итоге получается непереводимая игра слов когда напился и хочется потрындеть :)

dsl
()
Ответ на: комментарий от fluorite

Заметь, ты пришёл в development. По сути ничего не сказал, но начал скатывать тему в «кококо».

По сути уже все сказали

ты ещё слово «очепятка» начни поправлять

судя по комментам ТС, это ни разу не опечатка

И давай без перехода на личности, ок?

leave ★★★★★
()
Ответ на: комментарий от leave

Вот к чему приводит

к чему? желанию подложить соломку заранее?

современный

спасибо, но я стар и не современный, еще в начале нулевых это уже считалось вербузным отстоем но мне нравится :D

когда «кококо паскаль и сишечка нинужны, все начинаем с жавы/бидона».

на самом деле так и есть, я начинал с паскаля и ассемблера лет 25 назад и не вижу ничего плохого в том что бы начинать с той-же явы - с точки зрения логики она прямее и проще чем сишечка
сишечка как раз начинает заходить ПОСЛЕ явы, подкидывая ребусы на ровном месте но подкупая некоторой лаконичностью и огромным простором для изобретения велосипедов :D

ТС, ты же знаешь, что правильно будет «verbose»?

ага, в данном случае вербально бузеть мне нравится больше чем оригинал

rukez ★★★★
() автор топика
Ответ на: комментарий от rukez

Вот к чему приводит

к чему? желанию подложить соломку заранее?

Господя… Товарищ возжелал классический «pure virtual» (обзывайте как хотите). Так как сам не C-шник, а плюсовик - в этом контексте не подскажу (не знаю - как уж там у них принято). Но выше уже были очень и очень разумные предложения. И если контролировать «factory function» - что ещё нужно? Все compile-time constraints превращаются в assertions, вот и всё.

bugfixer ★★★★
()
Ответ на: комментарий от leave

По сути уже все сказали

Это повод скатывать тему в «кококо»?

И давай без перехода на личности, ок?

Ок. Ещё было бы чудесно, если бы модераторы не набрасывали. Хотя бы в development.

fluorite ★★★★★
()