‏نمایش پست‌ها با برچسب cpp. نمایش همه پست‌ها
‏نمایش پست‌ها با برچسب cpp. نمایش همه پست‌ها

۱۳۹۸/۱۲/۰۱

نوشته شده توسط Reza Alizadeh Majd
دسته بندي: , , ,

توی این سری میخوام نکاتی رو در مورد برنامه نویسی به زبان C++ بطور مختصر و با زبان ساده توضیح بدم.
تو اولین پست از این سری میخوام راجع به rvalue ها در c++ بنویسم. یکی از مواردی که برای خیلی از برنامه‌نویس‌ها ممکنه مقداری نامفهوم باشه.

مفهوم rvalue ها در c++ به مقادیر موقتی گفته میشه که به طور معمول در سمت راست یک expression قرار میگیرن. میشه اونها رو به یک متغیر اختصاص داد، اما نمیشه مقداری رو به اونها تخصیص داد. مقادیر عددی یا فراخوانی توابع مثال هایی از استفاده از rvalue ها در c++ هستش. تو مثال زیر `1` و `foo()` مثال هایی از rvalue هاست که برای مقداردهی اولیه متغیرهای `a` و `b` استفاده شده. به `a` و `b` در این مثال `lvalue` گفته میشه.


int a = 1;
int b = foo();


همچنین مثال های زیر نمونه هایی از مقدادهی نادرست بوده که امکان موقع کامپایل با خطا روبرو میشه:

1 = c; // error
foo() = d; // error

حالا چطور تعیین میکنیم که یک متغیر موقتی هستش؟‌ بطور کلی مقداری رو موقتی میدونیم که متغیری به اون مرتبط نشده باشه.

Int bar = 1; // bar is a name, so it is an lvalue.
2; // 2 is an integer literal, it has no name (no identifier). It's an rvalue.

مورد دیگه ای که میتونیم برای تشخیص rvalue ها استفاده کنیم اپراتور آدرس `&` هستش. توی c++ نمیتونیم آدرس rvalue ها در حافظه رو بگیریم.

int a = 1;
&a; // Works, address of a stack-allocated variable.
&1; // Error, 1 is an integer literal.
&foo(); // Error, cannot take address of the temporary result of a function call.

یکی از جاهایی که rvalue ها به ما کمک میکنن move semantics هستش که تو پست های بعدی راجع بهش صحبت میکنیم.

۱۳۹۸/۱۰/۲۹

نوشته شده توسط Reza Alizadeh Majd
دسته بندي: , ,

خوب باید اعتراف کنم بعد از این همه سال که دارم سی پلاس پلاس کد میزنم بازم کلی نکته راجع به این زبان باستانی وجود داره که ازشون بی خبرم و هر از گاهی با یه سری از اونها روبرو میشم. یکی از این موارد رو که امروز توی کار بهش برخوردم یه سری استفاده خاص از ماکروها هستش.

تعریف ماکرو

طبق تعریف ارائه شده در مستندات GCC، ماکرو در حقیقت تکه کدی هستش که براش نامی رو در نظر میگیریم. هرجا از برنامه که از این نام استفاده کرده باشیم کامپایلر قبل از شروع به کامپایل اون رو با تکه کد مربوطه جایگزین میکنه.

ما بطور کلی دو نوع استفاده از ماکرو داریم:
  • نوع اول object-like macros: که برای مشخص کردن مقادیر استفاده میکنیم.
  • نوع دوم function-like macros: ماکرو هایی که برای جایگزاری یه قطعه کد در برنامه استفاده میشن.

موارد معمول استفاده از ماکروها

حالا که یه تعریف کلی از ماکرو ارائه دادیم بریم سراغ استفاده های مختلف اون در برنامه ها:

۱. تعریف مقادیر ثابت پیش‌فرض

یکی از استفاده هایی که از ماکرو میشه اینه که مقادیر ثابت رو در بخشی از برنامه تعریف کنیم و از اون در جاهای مختلف برنامه استفاده کنیم:

#define PORT 8080
#define LOG_PATH "/path/foo/bar"
#define DEBUG_MODE 1

با تعریف این مقادیر ثابت در بخشی از برنامه و استفاده از نام ماکرو در بقیه برنامه دیگه لازم نیست برای تغییر یک متغیر کد برنامه رو شخم بزنیم هر استفاده ای رو اصلاح کنیم و فقط محل تعریف ماکرو رو اصلاح میکنیم.

همچنین میتونیم با استفاده از pre-processor flags برنامه رو با توجه به محیط مقصد کامپایل کنیم:



کامپایل بصورت پیش‌فرض:

$ gcc macro.c
$ ./a.out
compiled with: default flag 

تغییر MYFLAG در هنگام کامپایل:

$ gcc -DMYFLAG='"modified flag"' macro.c
$ ./a.out
compiled with: modified flag

۲. تعریف یک عملیات خاص توسط ماکرو

گاهی برای انجام یک عملیات تکراری میتونیم بخشی از کد رو بصورت ماکرو بنویسیم و اون رو با ورودی‌های مختلف فراخوانی کنیم.

#define MAX(a,b) (a>b ? a : b)
#define EXISTS(array, vector)  (vector.find(value) != vector.end())

از اونجایی که کد مرتبط با ماکرو قبل از کامپایل با ماکرو جایگزین میشه سرعت برنامه در این حالت بالاتر میره، البته این مورد در مقایسه با توابع inline فرق خاصی نداره و تنها تفاوتش در اینه که توابع inline توسط کامپایلر مدیریت میشن ولی ماکرو ها توسط pre-processor مدیریت میشن.

امکانات قابل استفاده در تعریف ماکرو ها

۱. تبدیل متغیر ماکرو به متن

در حالت عادی متغیرها / عبارت هایی رو که به ماکرو پاس میدیم اول محاسبه (evaluate) میشن و بعد به بدنه ماکرو ارسال میشن، ولی اگه بخوایم ورودی که به ماکرو ارسال شده رو داشته باشیم میتونیم با اضافه کردن کاراکتر `#` قبل از ورودی مورد نظر اون رو بصورت یک رشته داشته باشیم:



خروجی برنامه:

$ gcc macro2.c
$ ./a.out
Warning: x == 1

۲. اتصال پارامترهای ماکرو

بعضی مواقع لازم داریم تا مقداری رو به پارامترهای یک ماکرو اضافه کنیم در این موارد میتونیم این مقاریر رو با استفاده از `##` به هم متصل کنیم. برای بهتر فهمیدن مورد استفاده این امکان ماکرو اون رو با یک مثال توضیح میدیم:‌



خروجی برنامه:

$ gcc macro3.c
$ ./a.out
quit command executed.
help command executed.

توی این مثال با استفاده از ماکرو `MKCMD` آرایه رو با name و function مربوطه بصورت خودکار initiate کردیم.

۳. ماکرو با آرگومان های متغیر

برخی مواقع لازمه که ماکرو ما تعداد متغیری ماکرو داشته باشه. برای این موضوع مثل تعریف توابع با پارامترهای متغیر نام ماکرو رو با `...` مشخص میکنیم و هنگام استفاده `__VA_ARGS__` رو بکار میبریم:

#define eprintf(…) fprintf (stderr, __VA_ARGS__)
...
eprintf ("%s:%d: ", input_file, lineno);  
// above line will be replaced with following:
//           fprintf (stderr, "%s:%d: ", input_file, lineno)

بعضی مواقع وقتی ماکرو ما خیلی پیچیده میشه میتونیم قبل از `...` یک نام رو برای بخش متغیر پارامترها تعیین کنیم و در ادامه از اون نام استفاده کنیم. فقط باید توجه کنیم که نمیتونیم این روش رو با همراه با __VA_ARGS__ استفاده کنیم.

#define eprintf(args...) fprintf(stderr, args)

ما همچنین میتونیم از پارامترهای با نام در کنار پارامترهای متغیر استفاده کنیم:

#define eprintf(format, …) fprintf (stderr, format, __VA_ARGS__)

نوشته شده توسط Reza Alizadeh Majd
دسته بندي: , , , , ,


یکی از ابزاری که در نبود IDE مخصوصا در محیط ریموت میتونه کمکمون کنه تا دلیل کرش کردن برنامه رو کشف کنیم استفاده مستقیم از خود GDB هستش.

برای مثال برنامه زیر رو که موقع اجرا خطا میده میخوایم با GDB دیباگ کنیم:

وقتی به صورت عادی برنامه رو اجرا کنیم با خطای زیر روبرو میشیم:‌

$ ./hello
Floating point exception

اجرای برنامه بوسیله GDB

برای اینکه برنامه رو با gdb اجرا کنیم کافیه تا اون رو به عنوان ورودی به GDB بفرستیم و بعد با دستور `run` برنامه رو اجرا کنیم:

$ gdb ./hello
(gdb) run




و موقع بروز خطا با استفاده از back trace به بررسی مشکل بپردازیم:

(gdb) bt


همونطور که میبینید GDB اطلاعاتی راجع متدی که خطا در اون پیش اومده در اختیارمون قرار میده.



ذخیره لاگ در فایل

برای این منظور باید قبل از اجرای برنامه با دستور run تنظیمات لاگ مربوط به GDB رو بصورت زیر انجام بدیم:


(gdb) set logging on
(gdb) set logging file /path/to/log/file


همچنین برای مشاهده تنظیمات فعلی لاگ GDB از دستور زیر میتونیم استفاده کنیم:

(gdb) show logging


نوشته شده توسط Reza Alizadeh Majd
دسته بندي: , ,

از این به بعد تصمیم دارم تا اونجا که امکان داشته باشه مواردی رو که در طول کار روزانه باهاشون برخورد دارم رو اینجا لاگ کنم شاید بعدا دوباره لازمم شد.


جدیدا بخاطر سیاست شرکت جدید مجبور شدم از eclipse به Jetbrains CLion سوییچ کنم و این یعنی استفاده از CMAKE بجای build system داخلی eclipse. یکی از مواردی که تو روز اول کار با `CLio بهش برخوردم لینک کردن library های خارجی به برنامه خودم بود. برای اینکار از pkg-config استفاده کردم و بعد از نصب library مورد نظر، موارد زیر رو باید به فایل CMakeList.txt خودمون اضافه کنیم.

find_package(PkgConfig REQUIRED)
pkg_check_modules(TITLE lib-name REQUIRED)

include_directories(${TITLE_INCLUDE_DIRS})
link_directories(${TITLE_LIBRARY_DIRS})
link_libraries(${TITLE_LIBRARIES})