Makro kullanarak kaynak dosya satırları sayılsın mı?


15

C / C ++ önişlemcisini kullanarak, kaynak dosyadaki satırları bir makroya veya bir tür derleme zamanı kullanılabilir değere saymak mümkün müdür? Örneğin ben yerine MAGIC1, MAGIC2ve MAGIC3kullanırken aşağıda ve bir şekilde değerini 4 olsun MAGIC3?

MAGIC1 // can be placed wherever you like before the relevant 
       // lines - either right before them, or in global scope etc.
foo(); MAGIC2
bar(); MAGIC2
baz(); MAGIC2
quux(); MAGIC2
// ... possibly a bunch of code here; not guaranteed to be in same scope ...
MAGIC3

Notlar:

  • Ön işlemcinin yeteneklerine yönelik derleyiciye özgü uzantılar kabul edilebilir, ancak istenmeyen bir durumdur.
  • Bu sadece C + 'nın aksine, bazı C ++ yardımıyla mümkün, bu da kabul edilebilir ama istenmeyen (yani C için işe yarayacak bir şey istiyorum).
  • Açıkçası bu, kaynak dosyayı bazı harici işlemci komut dosyası aracılığıyla çalıştırarak yapılabilir, ancak istediğim bu değil.

6
Geçerli satır numarasını temsil eden bir makro__LINE__ var
ForceBru

2
__COUNTER__Ve / veya mı arıyorsunuz BOOST_PP_COUNTER?
KamilCuk

11
Çözmeniz gereken asıl sorun nedir ? buna ne için ihtiyacın var?
Bazı programcı ahbap

1
Does bu yardım?
user1810087

1
@PSkocik: Derleme zamanı sabiti olarak kullanabileceğim bir şey istiyorum, örneğin kodumun int arr[MAGIC4]daha önce sayılan bazı bölümlerindeki satır sayısını almak için.
einpoklum

Yanıtlar:


15

Orada __LINE__çizgi belirdi edilir için size bir tamsayı verir önişlemci makro. Değerini bir satırda, sonra da bazı satırlarda alıp karşılaştırabilirsiniz.

static const int BEFORE = __LINE__;
foo();
bar();
baz();
quux();
static const int AFTER = __LINE__;
static const int COUNT = AFTER - BEFORE - 1; // 4

Kaynak satırlarından ziyade bir şeyin oluşumunu saymak istiyorsanız , GCC ve MSVC gibi bazı derleyiciler __COUNTER__tarafından desteklenen standart olmayan bir seçenek olabilir .

#define MAGIC2_2(c)
#define MAGIC2(c) MAGIC2_2(c)
static const int BEFORE = __COUNTER__;
void foo(); MAGIC2(__COUNTER__);
void bar(
    int multiple,
    float lines); MAGIC2(__COUNTER__);
void baz(); MAGIC2(__COUNTER__);
void quux(); MAGIC2(__COUNTER__);
static const int AFTER = __COUNTER__;
static const int COUNT = AFTER - BEFORE - 1; // 4

İlk değerini, __COUNTER__daha önce kaynak dosyada veya dahil edilmiş bir üstbilgide kullanılmış olabileceği için aldım .

C ++ yerine C ++ 'da sabit değişkenler üzerinde sınırlamalar vardır, bu nedenle enumbunun yerine bir kullanılabilir.

enum MyEnum
{
    FOO = COUNT // C: error: enumerator value for ‘FOO’ is not an integer constant
};

Sabitin yerine koymak enum:

enum {BEFORE = __LINE__};
foo();
bar();
baz();
quux();
enum { COUNT = __LINE__ - BEFORE - 1};
enum MyEnum
{
    FOO = COUNT // OK
};

Bence bu sadece önişlemciyle alabileceğiniz en yakın şey. Önişlemci tek geçişlidir, bu nedenle daha sonra hesaplanan bir değeri geri gönderemezsiniz, ancak genel değişken başvuruları çalışır ve bunları optimize etmelidir. Tamsayı sabitleri ifadelerinde çalışmazlar, ancak sayım için gerekli olmayacak şekilde kodu yapılandırmak mümkün olabilir.
PSkocik

2
__COUNTER__C veya C ++ 'da standart değildir. Belirli derleyicilerle çalıştığını biliyorsanız, bunları belirtin.
Peter

@einpoklum hayır BEFOREve AFTERmakrolar değil
Alan Birtles

Bu çözümün sayaç olmayan sürümüyle ilgili bir sorun var: Öncesi ve sonrası yalnızca kaynak satırlarıyla aynı kapsamda kullanılabilir. "Örneğin" snippet'imi, bunun bir sorun olduğunu yansıtacak şekilde düzenledi.
einpoklum

1
@ user694733 Gerçek soru [C ++] olarak etiketlendi. C enum sabitleri için çalışır.
Ateş Lancer

9

OP'nin isteğinin makroları kullanmak olduğunu biliyorum, ancak bunu makroları içermeyen başka bir yol eklemek istiyorum.

C ++ 20 source_location, kaynak kodu hakkında dosya adları, satır numaraları ve işlev adları gibi belirli bilgileri temsil eden sınıfı tanıtır . Bu durumda bunu kolayca kullanabiliriz.

#include <iostream>
#include <source_location>

static constexpr auto line_number_start = std::source_location::current().line();
void foo();
void bar();
static constexpr auto line_number_end = std::source_location::current().line();

int main() {
    std::cout << line_number_end - line_number_start - 1 << std::endl; // 2

    return 0;
}

Ve burada canlı örnek .


Makrolar olmadan makrolardan daha iyidir. Ancak - bu yaklaşımla, hat sayımını yalnızca saydığım satırlarla aynı kapsamda kullanabilirim. Ayrıca - source_locationC ++ 20 ile deneysel olmak?
einpoklum

Makro içermeyen çözümün makrolardan çok daha iyi olduğunu kabul ediyorum. source_locationartık resmi olarak C ++ 20'nin bir parçası. Buradan kontrol edin . Godbolt.org'da gcc derleyicisinin sürümünü zaten deneysel olmayan bir şekilde destekleyen bir sürüm bulamadım . İfadenizi biraz daha açıklayabilir misiniz - satır sayısını yalnızca saydığım satırlarla aynı kapsamda kullanabilir miyim ?
NutCracker

Önerinizi bir işlev içine koyduğumu varsayalım (yani sayılan satırlar bildirimler değil, çağrılardır). İşe yarıyor - ama sadece bu kapsamda line_number_startve line_number_endbaşka hiçbir yerde yok. Başka bir yerde istiyorsam, çalışma zamanında geçmem gerekiyor - ki bu amacı yendi.
einpoklum

Standardın burada sağladığı örneğe bir göz atın . Varsayılan argüman ise, hala derleme zamanının bir parçasıdır, değil mi?
NutCracker

Evet, ancak bu line_number_endderleme zamanında kapsamı dışında görünmez. Yanlışsam düzelt.
einpoklum

7

Tamlık için: MAGIC2Her satırdan sonra eklemek istiyorsanız, aşağıdakileri kullanabilirsiniz __COUNTER__:

#define MAGIC2 static_assert(__COUNTER__ + 1, "");

/* some */     MAGIC2
void source(); MAGIC2
void lines();  MAGIC2

constexpr int numberOfLines = __COUNTER__;

int main()
{
    return numberOfLines;
}

https://godbolt.org/z/i8fDLx (döner 3)

Öğesinin başlangıç ​​ve bitiş değerlerini kaydederek yeniden kullanılabilir hale getirebilirsiniz __COUNTER__.

Genel olarak bu gerçekten hantal. Ayrıca, önişlemci yönergeleri içeren veya //yorumlarla biten satırları sayamazsınız . Bunun __LINE__yerine, diğer cevaba bakın.


1
neden kullanıyorsunuz static_assert?
idclev 463035818

1
Bu kaynak dosyaya "9" verdi __COUNTER__, diğer başlıklar, vb kullanabilirsiniz gibi başlangıçta hala sıfır olduğunu varsayamazsınız.
Yangın Lancer

__COUNTER__iki kez değerini kullanmanız ve farkı
almanız gerekir

1
@ Önceden __COUNTER__bilinen
Ateş Lancer

7

Farklı sayaçlara izin veren biraz daha sağlam bir çözüm (birbirine karışmadıkları ve __COUNTER__başka görevler için kullanmadıkları sürece ):

#define CONCATENATE(s1, s2) s1##s2
#define EXPAND_THEN_CONCATENATE(s1, s2) CONCATENATE(s1, s2)

#define COUNT_THIS_LINE static_assert(__COUNTER__ + 1, "");
#define START_COUNTING_LINES(count_name) \
  enum { EXPAND_THEN_CONCATENATE(count_name, _start) = __COUNTER__ };
#define FINISH_COUNTING_LINES(count_name) \
  enum { count_name = __COUNTER__ - EXPAND_THEN_CONCATENATE(count_name, _start) - 1 };

Bu, uygulama ayrıntılarını gizler (makroların içinde saklasa da ...). @ MaxLanghof'un cevabının genelleştirilmesidir. __COUNTER__Bir sayım başlattığımızda sıfır olmayan bir değere sahip olabileceğini unutmayın .

İşte nasıl kullanılır:

START_COUNTING_LINES(ze_count)

int hello(int x) {
    x++;
    /* some */     COUNT_THIS_LINE
    void source(); COUNT_THIS_LINE
    void lines();  COUNT_THIS_LINE
    return x;
}

FINISH_COUNTING_LINES(ze_count)

int main()
{
    return ze_count;
}

Ayrıca, bu geçerli C - ön işlemciniz destekliyorsa __COUNTER__, yani.

GodBolt üzerinde çalışır .

İçinde sayaçları yerleştirerek - Eğer C kullanıyorsanız ++, hatta genel ad alanını kirletmez için bu çözüm değiştirebilir namespace macro_based_line_counts { ... }veya namespace detail) vb


5

Yorumunuza dayanarak, C veya C ++ 'da (derleme zamanı) bir dizi boyutu belirtmek istiyorsanız,

int array[]; //incomplete type
enum{ LINE0 = __LINE__ }; //enum constants are integer constant expressions
/*lines to be counted; may use array (though not sizeof(array)) */
/*...*/
int array[ __LINE__ - LINE0 ]; //complete the definition of int array[]

sizeof(array)Aradaki çizgilere ihtiyacınız varsa , bunu statik bir değişken referansıyla değiştirebilirsiniz (kesinlikle bir tamsayı sabit ifadesi olması gerekmedikçe) ve optimize edici bir derleyici aynı şekilde davranmalıdır (statik değişkenin yerleştirilmesi ihtiyacını ortadan kaldırır) bellekte)

int array[]; static int count;
enum{ LINE0 = __LINE__ }; //enum constants are integer constant expressions
//... possibly use count here
enum { LINEDIFF = __LINE__ - LINE0 }; 
int array[ LINEDIFF ]; /*complete the definition of int array[]*/ 
static int count = LINEDIFF; //fill the count in later

A __COUNTER__tabanlı çözümün aksine A tabanlı çözüm (bu uzantı varsa) __LINE__aynı şekilde çalışır.

constexprC ++ 's da iyi çalışmalı enum, ancak enumdüz C de çalışacaktır (yukarıdaki benim çözüm düz bir C çözümdür).


Bu yalnızca satır sayımı kullanımım sayılan satırlarla aynı kapsamdaysa çalışır. IIANM. Not Sorun olabileceğini vurgulamak için sorumu hafifçe düzenledim.
einpoklum

1
@einpoklum __COUNTER__Tabanlı bir çözümün de sorunları var: Sihirli makronuzun __COUNTER__en azından kullanımınızı bitirmeden tek kullanıcı olmasını umarsınız __COUNTER__. Sorun temel olarak __COUNTER__/__LINE__, önişlemci özellikleri olan basit gerçeklere gelir ve önişlemci bir geçişte çalışır, böylece daha sonra __COUNTER__/ 'ya dayalı bir tamsayı sabit ifadesini geri alamazsınız __LINE__. Tek yol (en azından C cinsinden) ilk etapta ihtiyaçtan kaçınmaktır, örneğin, boyutsuz ileri dizi bildirimleri kullanarak (eksik yazılan dizi bildirimleri).
PSkocik

1
Kayıt için, \ etkilemez __LINE__- bir satır sonu varsa __LINE__artar. Örnek 1 , örnek 2 .
Max Langhof

@MaxLanghof Teşekkürler. Farkında değildim. Sabit.
PSkocik
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.