رفتن به مطلب
جامعه‌ی برنامه‌نویسان مُدرن ایران
  • 0
قاسم رمضانی منش

حجم بالا در کد‌های کتابخانه استاندارد

سوال

ارسال شده در (ویرایش شده)

با سلام.

درحال بررسی کدهای کتابخانه‌های استاندارد سی‌پلاس‌پلاس بودم ، که متوجه موردی شدم ؛ تقریبا بیشتر توابع و کلاس‌هایی که از کتابخانه‌های استاندارد استفاده میکنیم دارای مقدار زیادی وابستگی به توابع و فایل‌های دیگر دارند. برای مثال تابع std::swap که برای جابه‌جایی دو نوع استفاده میشود به این‌صورت میباشد :

template<typename _Tp, size_t _Nm>
  inline
  typename std::enable_if<__is_swappable<_Tp>::value>::type
  swap(_Tp (&__a)[_Nm], _Tp (&__b)[_Nm])
  noexcept(std::__is_nothrow_swappable<_Tp>::value)
  {
    for (size_t __n = 0; __n < _Nm; ++__n)
      swap(__a[__n], __b[__n]);
  }

که برای کامپایل نیاز به این موارد در دو فایل move.h و type_traits دارند :

template<typename _Tp, _Tp __v>
  struct integral_constant
  {
    static constexpr _Tp                  value = __v;
    typedef _Tp                           value_type;
    typedef integral_constant<_Tp, __v>   type;
    constexpr operator value_type() const noexcept { return value; }
#if __cplusplus > 201103L

#define __cpp_lib_integral_constant_callable 201304

    constexpr value_type operator()() const noexcept { return value; }
#endif
  };

  template<bool __v>
    using __bool_constant = integral_constant<bool, __v>;

typedef integral_constant<bool, true>     true_type;
  typedef integral_constant<bool, false>    false_type;

template<bool, typename _Tp = void>
  struct enable_if
  { };

  namespace __swappable_details {
    using std::swap;

    struct __do_is_swappable_impl
    {
      template<typename _Tp, typename
               = decltype(swap(std::declval<_Tp&>(), std::declval<_Tp&>()))>
        static true_type __test(int);

      template<typename>
        static false_type __test(...);
    };

    struct __do_is_nothrow_swappable_impl
    {
      template<typename _Tp>
        static __bool_constant<
          noexcept(swap(std::declval<_Tp&>(), std::declval<_Tp&>()))
        > __test(int);

      template<typename>
        static false_type __test(...);
    };

  }

  template<typename _Tp>
    struct __is_swappable_impl
    : public __swappable_details::__do_is_swappable_impl
    {
      typedef decltype(__test<_Tp>(0)) type;
    };

  template<typename _Tp>
    struct __is_swappable
    : public __is_swappable_impl<_Tp>::type
    { };

    template<typename _Tp>
      struct __is_nothrow_swappable_impl
      : public __swappable_details::__do_is_nothrow_swappable_impl
      {
        typedef decltype(__test<_Tp>(0)) type;
      };

  template<typename _Tp>
    struct __is_nothrow_swappable
    : public __is_nothrow_swappable_impl<_Tp>::type
    { };

خب ! سوال اول بنده اینجاس که در چنین مواردی ، بهتر نیست که تابعstd::swap را با توجه به نیازی که داریم خودمان پیاده‌سازی کنیم ؟ و اینکه آیا این حجم از کد و استفاده از template ها هزینه پِرفُورْمَنْس زیادی ندارد ؟

و سوال دوم :

تمام این کدها در دو فایل move.h و type_traits قرار دارد (که مسلماً این فایل ها هم وابستگی‌های دیگری به دیگر فایل‌ها دارند). آیا ما نمی‌توانیم مثلا فقط تابع std::swap را در برنامه‌ی خود فراخوانی کنیم که این حجم از کد احتیاج به کامپایل نداشته باشد ؟

برای نمونه در زبان برنامه‌نویسی پایتون ، با استفاده از دستور import ما یک ماژول را وارد برنامه میکنیم :

import time

در این روش تمام ماژول time به فایل‌ما اضافه خواهند شد. درصورتی که ما فقط از ماژول time نیاز به تابع sleep داشته باشیم کافی است که از قابلت from ... import ... استفاده کنیم :

from time import sleep

آیا این حرکت در C++ نیز امکان‌پذیر هست ؟

ویرایش شده در توسط قاسم رمضانی منش

به اشتراک گذاری این ارسال


لینک به ارسال
به اشتراک گذاری در سایت های دیگر

5 پاسخ به این سوال تا کنون داده شده است

پست های پیشنهاد شده

  • 2

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

header.hpp:
static const SomeComplexStruct;

در این صورت خوب مسلماً فایلی که این هدر رو اینکلود کرده باشه حافظهٔ بزرگتری اشغال می‌کنه (هم توی ایمیج هم توی رانتایم). ولی در حالت کلی همهٔ چیزهایی که توی هدر هستند کامپایل نمیشن. مثلا اگر هدرها تماماً از اعلان‌های کلاس‌ها و توابع تشکیل شده باشند چیزی کامپایل نمیشه. (معمولاً داخل هدر فقط اعلان قرار داده میشه)

 

مثال:

// header.hpp
struct bar {
   int x;
   int y;
   double z;
};

class foo {
public:
   bar x;
   bar y;
   bar z;
};

#include <iostream>
#include "header.hpp" // does not matter

int main() {
   std::cout << "Hello World!\n";
   return 0;
}

خروجی هر دو حالت:

        .file   "main.cpp"
        .text
        .section        .rodata
        .type   _ZStL19piecewise_construct, @object
        .size   _ZStL19piecewise_construct, 1
_ZStL19piecewise_construct:
        .zero   1
        .local  _ZStL8__ioinit
        .comm   _ZStL8__ioinit,1,1
.LC0:
        .string "Hello World!\n"
        .text
        .globl  main
        .type   main, @function
main:
.LFB1493:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        leaq    .LC0(%rip), %rsi
        leaq    _ZSt4cout(%rip), %rdi
        call    _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@PLT
        movl    $0, %eax
        popq    %rbp
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE1493:
        .size   main, .-main
        .type   _Z41__static_initialization_and_destruction_0ii, @function
_Z41__static_initialization_and_destruction_0ii:
.LFB1977:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        subq    $16, %rsp
        movl    %edi, -4(%rbp)
        movl    %esi, -8(%rbp)
        cmpl    $1, -4(%rbp)
        jne     .L5
        cmpl    $65535, -8(%rbp)
        jne     .L5
        leaq    _ZStL8__ioinit(%rip), %rdi
        call    _ZNSt8ios_base4InitC1Ev@PLT
        leaq    __dso_handle(%rip), %rdx
        leaq    _ZStL8__ioinit(%rip), %rsi
        movq    _ZNSt8ios_base4InitD1Ev@GOTPCREL(%rip), %rax
        movq    %rax, %rdi
        call    __cxa_atexit@PLT
.L5:
        nop
        leave
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE1977:
        .size   _Z41__static_initialization_and_destruction_0ii, .-_Z41__static_initialization_and_destruction_0ii
        .type   _GLOBAL__sub_I_main, @function
_GLOBAL__sub_I_main:
.LFB1978:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        movl    $65535, %esi
        movl    $1, %edi
        call    _Z41__static_initialization_and_destruction_0ii
        popq    %rbp
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE1978:
        .size   _GLOBAL__sub_I_main, .-_GLOBAL__sub_I_main
        .section        .init_array,"aw"
        .align 8
        .quad   _GLOBAL__sub_I_main
        .hidden __dso_handle
        .ident  "GCC: (Ubuntu 7.3.0-27ubuntu1~18.04) 7.3.0"
        .section        .note.GNU-stack,"",@progbits

 

  • ترکوندی! 1
  • تشکر شده 1

به اشتراک گذاری این ارسال


لینک به ارسال
به اشتراک گذاری در سایت های دیگر
  • 1
در 12 ساعت قبل، قاسم رمضانی منش گفته است :

خب ! سوال اول بنده اینجاس که در چنین مواردی ، بهتر نیست که تابعstd::swap را با توجه به نیازی که داریم خودمان پیاده‌سازی کنیم ؟ و اینکه آیا این حجم از کد و استفاده از template ها هزینه پِرفُورْمَنْس زیادی ندارد ؟

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

در 12 ساعت قبل، قاسم رمضانی منش گفته است :

تمام این کدها در دو فایل move.h و type_traits قرار دارد (که مسلماً این فایل ها هم وابستگی‌های دیگری به دیگر فایل‌ها دارند). آیا ما نمی‌توانیم مثلا فقط تابع std::swap را در برنامه‌ی خود فراخوانی کنیم که این حجم از کد احتیاج به کامپایل نداشته باشد ؟

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

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

معمولا هم به همین دلیل توصیه میشود که مثلا بجای استفاده از دستور using namespace std دقیقا به تابعی که لازم دارید اشاره کنید std::cout دراین روش در خروجی باینری فایل تاثیر گذار خواهد بود.

پیشنهاد میکنم این لینکها را هم مطالعه کنید.

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

 

  • هاها! 1

به اشتراک گذاری این ارسال


لینک به ارسال
به اشتراک گذاری در سایت های دیگر
  • 0

اضافه کردن فایل‌های هدر هیچ تأثیری در حجم برنامهٔ نهایی نداره. فقط توابعی که کامپایل شدن (کد باینری برای اون‌ها تولید شده) در باینری نهایی جا می‌گیرن.

  • پسندیدن 1
  • تشکر شده 1

به اشتراک گذاری این ارسال


لینک به ارسال
به اشتراک گذاری در سایت های دیگر
  • 0
ارسال شده در (ویرایش شده)
در ۱ ساعت قبل، سروش ربیعی گفته است :

اضافه کردن فایل‌های هدر هیچ تأثیری در حجم برنامهٔ نهایی نداره

(عذرخواهی میکنم مطالبی ارسالی این پست به دلیلی خروجی اسمبلی کدها طولانی و خارج از حوصله است. لذا متن‌های بین کدها با سبک دُرُشت و اندازه ۱۸ نوشته شده است)

خب مگه اون هدر فایلی که اضافه میکنیم کامپایل نمیشه ؟ 

الان یک نمونه تستی که من نوشتم :

header.hpp

class Base{
  public :
    Base(){std::cout << "BASE#c";}
    ~Base(){std::cout << "BASE#d";}
};
void AnotherTest (void){}
void AndAnotherTest(void){}

main.cpp

#include <iostream>
#include "header.hpp"

int main(){
	return 0;
}

خروجی اسمبلی زیر را تولید کرده :

	.file	"main.cpp"
	.text
	.section	.rodata
	.type	_ZStL19piecewise_construct, @object
	.size	_ZStL19piecewise_construct, 1
_ZStL19piecewise_construct:
	.zero	1
	.local	_ZStL8__ioinit
	.comm	_ZStL8__ioinit,1,1
	.text
	.globl	_Z11AnotherTestv
	.type	_Z11AnotherTestv, @function
_Z11AnotherTestv:
.LFB1524:
	.cfi_startproc
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	nop
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE1524:
	.size	_Z11AnotherTestv, .-_Z11AnotherTestv
	.globl	_Z14AndAnotherTestv
	.type	_Z14AndAnotherTestv, @function
_Z14AndAnotherTestv:
.LFB1525:
	.cfi_startproc
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	nop
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE1525:
	.size	_Z14AndAnotherTestv, .-_Z14AndAnotherTestv
	.globl	main
	.type	main, @function
main:
.LFB1526:
	.cfi_startproc
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	movl	$0, %eax
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE1526:
	.size	main, .-main
	.type	_Z41__static_initialization_and_destruction_0ii, @function
_Z41__static_initialization_and_destruction_0ii:
.LFB2010:
	.cfi_startproc
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	subq	$16, %rsp
	movl	%edi, -4(%rbp)
	movl	%esi, -8(%rbp)
	cmpl	$1, -4(%rbp)
	jne	.L7
	cmpl	$65535, -8(%rbp)
	jne	.L7
	movl	$_ZStL8__ioinit, %edi
	call	_ZNSt8ios_base4InitC1Ev
	movl	$__dso_handle, %edx
	movl	$_ZStL8__ioinit, %esi
	movl	$_ZNSt8ios_base4InitD1Ev, %edi
	call	__cxa_atexit
.L7:
	nop
	leave
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE2010:
	.size	_Z41__static_initialization_and_destruction_0ii, .-_Z41__static_initialization_and_destruction_0ii
	.type	_GLOBAL__sub_I__Z11AnotherTestv, @function
_GLOBAL__sub_I__Z11AnotherTestv:
.LFB2011:
	.cfi_startproc
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	movl	$65535, %esi
	movl	$1, %edi
	call	_Z41__static_initialization_and_destruction_0ii
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE2011:
	.size	_GLOBAL__sub_I__Z11AnotherTestv, .-_GLOBAL__sub_I__Z11AnotherTestv
	.section	.init_array,"aw"
	.align 8
	.quad	_GLOBAL__sub_I__Z11AnotherTestv
	.hidden	__dso_handle
	.ident	"GCC: (GNU) 8.2.1 20181105 (Red Hat 8.2.1-5)"
	.section	.note.GNU-stack,"",@progbits

و کد زیر :

#include <iostream>
#include "header.hpp"

int main(){
	AndAnotherTest();
	return 0;
}

خروجی اسمبلی زیر را تولید کرده :

	.file	"main.cpp"
	.text
	.section	.rodata
	.type	_ZStL19piecewise_construct, @object
	.size	_ZStL19piecewise_construct, 1
_ZStL19piecewise_construct:
	.zero	1
	.local	_ZStL8__ioinit
	.comm	_ZStL8__ioinit,1,1
	.text
	.globl	_Z11AnotherTestv
	.type	_Z11AnotherTestv, @function
_Z11AnotherTestv:
.LFB1524:
	.cfi_startproc
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	nop
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE1524:
	.size	_Z11AnotherTestv, .-_Z11AnotherTestv
	.globl	_Z14AndAnotherTestv
	.type	_Z14AndAnotherTestv, @function
_Z14AndAnotherTestv:
.LFB1525:
	.cfi_startproc
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	nop
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE1525:
	.size	_Z14AndAnotherTestv, .-_Z14AndAnotherTestv
	.globl	main
	.type	main, @function
main:
.LFB1526:
	.cfi_startproc
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	call	_Z14AndAnotherTestv
	movl	$0, %eax
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE1526:
	.size	main, .-main
	.type	_Z41__static_initialization_and_destruction_0ii, @function
_Z41__static_initialization_and_destruction_0ii:
.LFB2010:
	.cfi_startproc
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	subq	$16, %rsp
	movl	%edi, -4(%rbp)
	movl	%esi, -8(%rbp)
	cmpl	$1, -4(%rbp)
	jne	.L7
	cmpl	$65535, -8(%rbp)
	jne	.L7
	movl	$_ZStL8__ioinit, %edi
	call	_ZNSt8ios_base4InitC1Ev
	movl	$__dso_handle, %edx
	movl	$_ZStL8__ioinit, %esi
	movl	$_ZNSt8ios_base4InitD1Ev, %edi
	call	__cxa_atexit
.L7:
	nop
	leave
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE2010:
	.size	_Z41__static_initialization_and_destruction_0ii, .-_Z41__static_initialization_and_destruction_0ii
	.type	_GLOBAL__sub_I__Z11AnotherTestv, @function
_GLOBAL__sub_I__Z11AnotherTestv:
.LFB2011:
	.cfi_startproc
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	movl	$65535, %esi
	movl	$1, %edi
	call	_Z41__static_initialization_and_destruction_0ii
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE2011:
	.size	_GLOBAL__sub_I__Z11AnotherTestv, .-_GLOBAL__sub_I__Z11AnotherTestv
	.section	.init_array,"aw"
	.align 8
	.quad	_GLOBAL__sub_I__Z11AnotherTestv
	.hidden	__dso_handle
	.ident	"GCC: (GNU) 8.2.1 20181105 (Red Hat 8.2.1-5)"
	.section	.note.GNU-stack,"",@progbits

و در کد اسمبلئ که هیچ یک از توابع فایلheader.hpp استفاده نشده است. خروجی اسمبلی توابع فایل header.hpp موجود هست :

[ghasem@clibcore tmp]$ cat --number OnlyHeader | egrep "(AnotherTest|AndAnotherTest)" 
    11		.globl	_Z11AnotherTestv
    12		.type	_Z11AnotherTestv, @function
    13	_Z11AnotherTestv:
    27		.size	_Z11AnotherTestv, .-_Z11AnotherTestv
    28		.globl	_Z14AndAnotherTestv
    29		.type	_Z14AndAnotherTestv, @function
    30	_Z14AndAnotherTestv:
    44		.size	_Z14AndAnotherTestv, .-_Z14AndAnotherTestv
    92		.type	_GLOBAL__sub_I__Z11AnotherTestv, @function
    93	_GLOBAL__sub_I__Z11AnotherTestv:
   109		.size	_GLOBAL__sub_I__Z11AnotherTestv, .-_GLOBAL__sub_I__Z11AnotherTestv
   112		.quad	_GLOBAL__sub_I__Z11AnotherTestv

و در کدی که فقط تابع AndAnotherTest() استفاده شده است :

[ghasem@clibcore tmp]$ cat --number UseOneFunction | egrep "(AnotherTest|AndAnotherTest)"
    11		.globl	_Z11AnotherTestv
    12		.type	_Z11AnotherTestv, @function
    13	_Z11AnotherTestv:
    27		.size	_Z11AnotherTestv, .-_Z11AnotherTestv
    28		.globl	_Z14AndAnotherTestv
    29		.type	_Z14AndAnotherTestv, @function
    30	_Z14AndAnotherTestv:
    44		.size	_Z14AndAnotherTestv, .-_Z14AndAnotherTestv
    55		call	_Z14AndAnotherTestv
    93		.type	_GLOBAL__sub_I__Z11AnotherTestv, @function
    94	_GLOBAL__sub_I__Z11AnotherTestv:
   110		.size	_GLOBAL__sub_I__Z11AnotherTestv, .-_GLOBAL__sub_I__Z11AnotherTestv
   113		.quad	_GLOBAL__sub_I__Z11AnotherTestv

 و تنها تفاوت این دو در فراخوانی تابع AndAnotherTest() هست :

[ghasem@clibcore output]$ cat --number JustHeader | egrep "(AnotherTest|AndAnotherTest)" > OnlyHeaderGREP
[ghasem@clibcore output]$ cat --number UseOneFunction | egrep "(AnotherTest|AndAnotherTest)" > UseOneFunctionGREP
[ghasem@clibcore output]$ diff JustHeader UseOneFunction
55d54
< 	call	_Z14AndAnotherTestv

در حالی که من از پاسخ شما @سروش ربیعی فهمیدم که گفتید هدرفایل‌ها تاثیری در حجم برنامه‌ٔ‌نهایی نداره. اما مثال بالا که زدم با وجود اینکه هیچ فراخوانی از توابع نشده بود بازم هم کد های هدرفایل کامپایل شد.

و اما بخشی که منو گیج کرده ! 

ما در این فایل هم هدرفایل iostream و هم header.hpp را وارد برنامه کرده ایم. توابعی که در header.hpp بود تماما کامپایل شده و در خروجی اسمبلی هست. اما خبری از توابع و دستورات iostream نیست. این خروجی اسمبلی بدون هدرفایل iostream هست :

	.file	"main.cpp"
	.text
	.globl	_Z11AnotherTestv
	.type	_Z11AnotherTestv, @function
_Z11AnotherTestv:
.LFB0:
	.cfi_startproc
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	nop
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE0:
	.size	_Z11AnotherTestv, .-_Z11AnotherTestv
	.globl	_Z14AndAnotherTestv
	.type	_Z14AndAnotherTestv, @function
_Z14AndAnotherTestv:
.LFB1:
	.cfi_startproc
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	nop
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE1:
	.size	_Z14AndAnotherTestv, .-_Z14AndAnotherTestv
	.globl	main
	.type	main, @function
main:
.LFB2:
	.cfi_startproc
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	movl	$0, %eax
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE2:
	.size	main, .-main
	.ident	"GCC: (GNU) 8.2.1 20181105 (Red Hat 8.2.1-5)"
	.section	.note.GNU-stack,"",@progbits

و تفاوت آن با خروجی اسمبلی‌ای که هدرفایل iostream وجود داشته است  :

[ghasem@clibcore output]$ diff WithIostream WithoutIostrea 
3,10d2
< 	.section	.rodata
< 	.type	_ZStL19piecewise_construct, @object
< 	.size	_ZStL19piecewise_construct, 1
< _ZStL19piecewise_construct:
< 	.zero	1
< 	.local	_ZStL8__ioinit
< 	.comm	_ZStL8__ioinit,1,1
< 	.text
14c6
< .LFB1518:
---
> .LFB0:
26c18
< .LFE1518:
---
> .LFE0:
31c23
< .LFB1519:
---
> .LFB1:
43c35
< .LFE1519:
---
> .LFE1:
48c40
< .LFB1520:
---
> .LFB2:
60c52
< .LFE1520:
---
> .LFE2:
62,113d53
< 	.type	_Z41__static_initialization_and_destruction_0ii, @function
< _Z41__static_initialization_and_destruction_0ii:
< .LFB2001:
< 	.cfi_startproc
< 	pushq	%rbp
< 	.cfi_def_cfa_offset 16
< 	.cfi_offset 6, -16
< 	movq	%rsp, %rbp
< 	.cfi_def_cfa_register 6
< 	subq	$16, %rsp
< 	movl	%edi, -4(%rbp)
< 	movl	%esi, -8(%rbp)
< 	cmpl	$1, -4(%rbp)
< 	jne	.L7
< 	cmpl	$65535, -8(%rbp)
< 	jne	.L7
< 	movl	$_ZStL8__ioinit, %edi
< 	call	_ZNSt8ios_base4InitC1Ev
< 	movl	$__dso_handle, %edx
< 	movl	$_ZStL8__ioinit, %esi
< 	movl	$_ZNSt8ios_base4InitD1Ev, %edi
< 	call	__cxa_atexit
< .L7:
< 	nop
< 	leave
< 	.cfi_def_cfa 7, 8
< 	ret
< 	.cfi_endproc
< .LFE2001:
< 	.size	_Z41__static_initialization_and_destruction_0ii, .-_Z41__static_initialization_and_destruction_0ii
< 	.type	_GLOBAL__sub_I__Z11AnotherTestv, @function
< _GLOBAL__sub_I__Z11AnotherTestv:
< .LFB2002:
< 	.cfi_startproc
< 	pushq	%rbp
< 	.cfi_def_cfa_offset 16
< 	.cfi_offset 6, -16
< 	movq	%rsp, %rbp
< 	.cfi_def_cfa_register 6
< 	movl	$65535, %esi
< 	movl	$1, %edi
< 	call	_Z41__static_initialization_and_destruction_0ii
< 	popq	%rbp
< 	.cfi_def_cfa 7, 8
< 	ret
< 	.cfi_endproc
< .LFE2002:
< 	.size	_GLOBAL__sub_I__Z11AnotherTestv, .-_GLOBAL__sub_I__Z11AnotherTestv
< 	.section	.init_array,"aw"
< 	.align 8
< 	.quad	_GLOBAL__sub_I__Z11AnotherTestv
< 	.hidden	__dso_handle

با یه نگاه کردن به  فایل iostream :

#ifndef _GLIBCXX_IOSTREAM
#define _GLIBCXX_IOSTREAM 1

#pragma GCC system_header

#include <bits/c++config.h>
#include <ostream>
#include <istream>

namespace std _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION
  extern istream cin;		
  extern ostream cout;		
  extern ostream cerr;		
  extern ostream clog;		

#ifdef _GLIBCXX_USE_WCHAR_T
  extern wistream wcin;		
  extern wostream wcout;	
  extern wostream wcerr;	
  extern wostream wclog;	
#endif
  static ios_base::Init __ioinit;

_GLIBCXX_END_NAMESPACE_VERSION
} 
#endif 

ظاهرا دلیل اون تفاوت هم مشخص هست. اما تکلیف مابقی هدرفایل هایی که داخل iostream وارد شده اند چیست ؟ مگه توابع و متغیر های آنها نیز کامپایل نمیشود ؟ پس چرا مثل خروجی کد اسمبلی‌ توابع موجود در header.hpp در سورس اسمبلی نهایی نیست ؟ (و باز عذرخواهی میکنم بابت طولانی بودن پست )

ویرایش شده در توسط قاسم رمضانی منش

به اشتراک گذاری این ارسال


لینک به ارسال
به اشتراک گذاری در سایت های دیگر
  • 0
ارسال شده در (ویرایش شده)

با تشکر از توضیحات شما @فرهاد شیری  .

در 1 ساعت قبل، فرهاد شیری گفته است :

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

بنده اون لینکی که پیشنهاد داده بودید و به‌علاوه موارد دیگری هم در این زمینه خوانده بودم. و دقیقاً سعی کردم مثالی مرتبط به این موضوع بزنم. پس یه نگاه دیگه به فایل هدری که مثال زده بودم بکنید :

class Base{
  public :
    Base(){std::cout << "BASE#c";}
    ~Base(){std::cout << "BASE#d";}
};
void AnotherTest (void){}
void AndAnotherTest(void){}

در فایل main.cpp من تنها یک بار تابع AndAnotherTest() را فراخوانی کرده بودم. که مسلماً طبق چیزی که گفتید :

در 1 ساعت قبل، فرهاد شیری گفته است :

خروجی باینری شما ترکیبی از کد های کامپایل شده شما خواهد بود به اضافه کلاسها و توابع استاندارد ++C که شما در برنامه خود استفاده کرده اید

و بنده به این صورت برداشت کردم : که ابتدا تمام کد های فایل هدر من کامپایل شده و سپس در زمان لینک ، لینکر می‌بایست تنها لینکی به تابع AndAnotherTest() داشته باشد. و فقط هم این تابع در خروجی کد من باشد.

من برای تست این موضوع از تعریف یک کلاس و دو تابع استفاده کردم. از آن‌جایی که میدونستم کلاس تنها یه نقشه از ساختمان است. پس تا زمانی که از این نقشه نمونه‌سازی نشوند ، نباید هیچ حافظه‌ای یا کامپایلی برای کد های این کلاس درنظر گرفته بشه. که تا این قسمت با توجه به خروجی اسمبلی من درست بود و حرفی از کلاس Base زده نشده بود. اما درمورد دوتا تابع بعدی که دقیقا هر دو کامپایل و درخروجی کد من بودن. در مقایسه هایی که کردم (که تماماً در طوماری که بالا ارسال کردم ، ذکر شده) متوجه شدم تنها تفاوت در فایلی که من تابع را فراخوانی و در فایلی که فراخوانی نکرده بودم تنها یک خط در خروجی اسمبلی بود ؛ اون‌هم دستور فراخوانی تابع AndAnotherTest() :

[ghasem@clibcore output]$ cat --number JustHeader | egrep "(AnotherTest|AndAnotherTest)" > OnlyHeaderGREP
[ghasem@clibcore output]$ cat --number UseOneFunction | egrep "(AnotherTest|AndAnotherTest)" > UseOneFunctionGREP
[ghasem@clibcore output]$ diff JustHeader UseOneFunction
55d54
< 	call	_Z14AndAnotherTestv

در مورد هدرفایل iostream هم. متغیرها و توابعی که به صورت static یا non-member function تعریف شده‌اند ، خروجی اسمبلی‌شان در فایل نهایی من هست. به غیر از tamplate-function و template-class و class ها که آن‌ها دقیقا برای کامپایل شدن نیاز به استفاده شدن را دارند.

 

الان دلیل این اتفاق  : وجود کد اسمبلی تابع استفاده نشده در فایل هدر ، در خروجی نهایی فایل ترجمه شده به اسمبلی من چیست ؟ و آیا راهی برای جلوگیری از این موضوع هست ؟

ویرایش شده در توسط قاسم رمضانی منش

به اشتراک گذاری این ارسال


لینک به ارسال
به اشتراک گذاری در سایت های دیگر

برای ارسال دیدگاه یک حساب کاربری ایجاد کنید یا وارد حساب خود شوید

برای اینکه بتوانید دیدگاهی ارسال کنید نیاز دارید که کاربر سایت شوید

ایجاد یک حساب کاربری

برای حساب کاربری جدید در سایت ما ثبت نام کنید. عضویت خیلی ساده است !

ثبت نام یک حساب کاربری جدید

ورود به حساب کاربری

دارای حساب کاربری هستید؟ از اینجا وارد شوید

ورود به حساب کاربری

  • کاربران آنلاین در این صفحه   0 کاربر

    هیچ کاربر عضوی،در حال مشاهده این صفحه نیست.

×