if you want to become a programmer professionally, I've always been told to start with C, then C++. C will teach you everything you need.
C++ can be a bit daunting at the beginning, pointers and references throw people off if not taught properly and simply. Classes can be confusing, the real problems are when your code base gets large, it's difficult keeping track of what your doing. Remember though these are languages and can really do little by themselves, you can only really do basic console calculations or file I/O. To do any GUI stuff you need to either use the win32 API or some third party toolkit such as Qt. As for games you would need to either learn openGl or DirectX which is when things get proper tricky.
Most people nowadays use engine such as unity or unreal.
In the old days most things revolved around efficiency. People would use say c++ with inline ASM for max efficiency. Nowadays this isn't such a problem as pcs are powerful and lots of ram.
Sorry, but this is, imo, terrible advice. C is the last place a new programmer should begin unless they're very specifically going into an area that is heavily dominated by C. It's lacking so many modern features you'll learn very little about programming today. And there are so many little gotchas and difficulties. Do you really want someone in this day and age to start off their programming journey learning about memory allocation when no modern language even requires that sort of thing nowadays. I'm pretty sure OP doesn't want to be a kernel writer or write low-level device drivers!
The reasoning being that C being such low level it gives you an excellent understanding of how languages interact with hardware and it gives you a much stronger foundation in other languages.
You're correct that modern languages don't need to worry about memory allocation or even setting the data type of a variable, that doesn't mean having a good understanding of it all is a bad thing to work from.
As I did say though, which you seemed to miss, if the op is looking to dabble and 'have a go' they would be better off with python. It's an easier language to get into and with less concerns regarding memory and data types.
Sorry, but this is, imo, terrible advice. C is the last place a new programmer should begin unless they're very specifically going into an area that is heavily dominated by C. It's lacking so many modern features you'll learn very little about programming today. And there are so many little gotchas and difficulties. Do you really want someone in this day and age to start off their programming journey learning about memory allocation when no modern language even requires that sort of thing nowadays. I'm pretty sure OP doesn't want to be a kernel writer or write low-level device drivers!
Classes are the last confusing thing in programming there is! Whatever OP chooses, it MUST be an Object Orientated language or what they'll learn is so at odds with real world programming that trying to transition will confuse the Hell out of them.
That's specific to games programming. And even then you still need a solid grounding in the language.
It's not so much that. Modern compilers are very good at optimizing code and handling this stuff automatically. They days of someone being able to hand-craft some low-level code and do a better job than the compiler are mostly gone. Especially for C++ or C#. Even PHP's "compiler" is pretty good as of PHP 7.
I assume you meant least and not last
C is a horrid place to learn really - though if you do master it you will be well setup for almost any language as you will have a good grasp of what is going on underneath many stock commands, etc. of other languages. I think it puts off more people than it helps though - I don't even recommend C++ as a starting point in this respect.
I'm actually not a big fan of the modern approach to object oriented programming a lot of it is unnecessarily sprawling partly in an attempt to make it more readable and obscure in implementation until you've learnt it - I've not found anything yet I can't do better with linked lists and some function pointers.
I think we mostly agree, but at the risk of repeating my earlier post I'd question how much real value you get from understanding what lies beneath many stock commands. Again, understanding it isn't a bad thing, but which is more useful - knowing you can type list.count('MyValue') and focusing on your program's logic, or knowing how in C you would write such a function and then never actually needing to do so because most modern languages have a way to do it built in?
I'd be fascinated to know how you managed encapsulation with linked lists and some function pointers.
#define U_MEMBER 0
#define U_START 1
#define U_END 2
#define GROUP_EQUIP 0
#define GROUP_POWER 1
#define GROUP_PERK 2
#define UF_DEFAULT 0
#define UF_HIDDEN 1
typedef enum
{
AT_MASK,
AT_BOOTS,
AT_GLOVES,
AT_POWER1,
AT_POWER2,
AT_MAX
} attribs_t;
typedef enum
{
MASK_NONE,
MASK_TEST,
MASK_END,
BOOTS_NONE,
BOOTS_TEST,
BOOTS_END,
UP_MAX
} upident_t
typedef struct stats_s
{
float health;
float damage;
float jump;
float tech;
float unused1;
float unused2;
float unused3;
} stats_t;
typedef struct slots_s
{
char *title;
char *backimage;
int atnum;
int group;
} slots_t;
extern slots_t slotlist[];
typedef struct upgrades_s
{
int unum; // upgrade ident
char *desc; // desciption
char *icon;
int utype;
stats_t *stat;
int flags; // disabled, etc.
int timer; // delay until can be reused - tech reduces this
void (*use)(struct edict_s *ent);
} upgrades_t;
extern upgrades_t upgradeslist[];
void Cmd_UpNext_f(edict_t *ent)
{
int i, c;
upgrades_t *up;
if (ent->client->showattrib == false)
return;
if ((ent->client->attribSel == AT_POWER1) && (level.time < ent->client->power1end))
{
gi.sound(ent, CHAN_VOICE, gi.soundindex("misc/menu3.wav"), 1, ATTN_STATIC, 0);
return;
}
if ((ent->client->attribSel == AT_POWER2) && (level.time < ent->client->power2end))
{
gi.sound(ent, CHAN_VOICE, gi.soundindex("misc/menu3.wav"), 1, ATTN_STATIC, 0);
return;
}
if ((ent->client->attribSel == AT_SPECIAL) && (level.time < ent->client->specialend))
{
gi.sound(ent, CHAN_VOICE, gi.soundindex("misc/menu3.wav"), 1, ATTN_STATIC, 0);
return;
}
if (ent->client->turret)
{
gi.sound(ent, CHAN_VOICE, gi.soundindex("misc/menu3.wav"), 1, ATTN_STATIC, 0);
return;
}
i = ent->client->pers.upgrades[ent->client->attribSel] + 1;
up = upgradeslist;
up += i;
// Never make non U_MEMBER entries hidden
c = 1;
while (1)
{
if (!(up->flags & UF_HIDDEN))
break;
up++;
c++;
}
if (up->utype == U_MEMBER)
{
ent->client->pers.upgrades[ent->client->attribSel]+=c;
gi.sound(ent, CHAN_VOICE, gi.soundindex("misc/menu2.wav"), 1, ATTN_STATIC, 0);
calcUpgrades(ent);
return;
}
gi.sound(ent, CHAN_VOICE, gi.soundindex("misc/menu3.wav"), 1, ATTN_STATIC, 0);
}
slots_t slotlist[] =
{
{
"Mask",
"tag1",
AT_MASK,
GROUP_EQUIP,
},
{
"Boots",
"tag1",
AT_BOOTS,
GROUP_EQUIP,
},
{ NULL }
};
stats_t stat_blank = {
/*resists, health, regen*/ 0, 0, 0,
/*health on kill, health per hit*/ 0, 0,
/*global damage bonus*/ 0,
/*MG,combat shotgun,SS,grenade,GL*/ 0, 0, 0, 0, 0,
/*RL,chaingun,HB,RG,turret*/ 0, 0, 0, 0, 0,
/*air, gravity*/ 0, 0,
/*tech*/ 0,
/*unused*/ 0, 0, 0
};
upgrades_t upgradeslist[] =
{
{
MASK_NONE, // upgrade id
"No Mask", // ui name
"i_fixme", // ui icon
U_START, // list
&stat_blank, // stats array
0, // flags i.e. DF_HIDDEN
0, // cooldown delay if used
NULL, // function if used
},
{
MASK_TEST,
"Test mask",
"p_rebreather",
U_MEMBER,
&stat_mask_recon,
0,
0,
NULL,
},
{
MASK_END, // dummy entry to mark the end of this group in the list
"No mask",
"i_fixme",
U_END,
&stat_blank,
0,
0,
NULL,
},
...
}
Do you have a GitHub harmony?
Performance and/or avoiding limitations or hardware bugs, etc. or, though potentially risky, making use of the way something works internally in an unintended way to pack additional data/flags, etc.
Obviously there is the use of structures, etc. in there was simplifying but I don't even think in the concept of things like encapsulation, interfaces, lambdas and closures, etc. in the way I program.
EDIT: Not sure if this will make any sense as it is a mixture of excerpts, with mismatching definitions and a lot of missing code due to the complexity of it and nothing that shows how it all comes together, of an RPGlite implementation I did for a Quake 2 mod - using plain C with some limitations - the sample function just shows iterating through the items available in a specific upgrade group (I'm aware there are a couple of bugs in the function):
Code:#define U_MEMBER 0 #define U_START 1 #define U_END 2 #define GROUP_EQUIP 0 #define GROUP_POWER 1 #define GROUP_PERK 2 #define UF_DEFAULT 0 #define UF_HIDDEN 1 typedef enum { AT_MASK, AT_BOOTS, AT_GLOVES, AT_POWER1, AT_POWER2, AT_MAX } attribs_t; typedef enum { MASK_NONE, MASK_TEST, MASK_END, BOOTS_NONE, BOOTS_TEST, BOOTS_END, UP_MAX } upident_t typedef struct stats_s { float health; float damage; float jump; float tech; float unused1; float unused2; float unused3; } stats_t; typedef struct slots_s { char *title; char *backimage; int atnum; int group; } slots_t; extern slots_t slotlist[]; typedef struct upgrades_s { int unum; // upgrade ident char *desc; // desciption char *icon; int utype; stats_t *stat; int flags; // disabled, etc. int timer; // delay until can be reused - tech reduces this void (*use)(struct edict_s *ent); } upgrades_t; extern upgrades_t upgradeslist[]; void Cmd_UpNext_f(edict_t *ent) { int i, c; upgrades_t *up; if (ent->client->showattrib == false) return; if ((ent->client->attribSel == AT_POWER1) && (level.time < ent->client->power1end)) { gi.sound(ent, CHAN_VOICE, gi.soundindex("misc/menu3.wav"), 1, ATTN_STATIC, 0); return; } if ((ent->client->attribSel == AT_POWER2) && (level.time < ent->client->power2end)) { gi.sound(ent, CHAN_VOICE, gi.soundindex("misc/menu3.wav"), 1, ATTN_STATIC, 0); return; } if ((ent->client->attribSel == AT_SPECIAL) && (level.time < ent->client->specialend)) { gi.sound(ent, CHAN_VOICE, gi.soundindex("misc/menu3.wav"), 1, ATTN_STATIC, 0); return; } if (ent->client->turret) { gi.sound(ent, CHAN_VOICE, gi.soundindex("misc/menu3.wav"), 1, ATTN_STATIC, 0); return; } i = ent->client->pers.upgrades[ent->client->attribSel] + 1; up = upgradeslist; up += i; // Never make non U_MEMBER entries hidden c = 1; while (1) { if (!(up->flags & UF_HIDDEN)) break; up++; c++; } if (up->utype == U_MEMBER) { ent->client->pers.upgrades[ent->client->attribSel]+=c; gi.sound(ent, CHAN_VOICE, gi.soundindex("misc/menu2.wav"), 1, ATTN_STATIC, 0); calcUpgrades(ent); return; } gi.sound(ent, CHAN_VOICE, gi.soundindex("misc/menu3.wav"), 1, ATTN_STATIC, 0); } slots_t slotlist[] = { { "Mask", "tag1", AT_MASK, GROUP_EQUIP, }, { "Boots", "tag1", AT_BOOTS, GROUP_EQUIP, }, { NULL } }; stats_t stat_blank = { /*resists, health, regen*/ 0, 0, 0, /*health on kill, health per hit*/ 0, 0, /*global damage bonus*/ 0, /*MG,combat shotgun,SS,grenade,GL*/ 0, 0, 0, 0, 0, /*RL,chaingun,HB,RG,turret*/ 0, 0, 0, 0, 0, /*air, gravity*/ 0, 0, /*tech*/ 0, /*unused*/ 0, 0, 0 }; upgrades_t upgradeslist[] = { { MASK_NONE, // upgrade id "No Mask", // ui name "i_fixme", // ui icon U_START, // list &stat_blank, // stats array 0, // flags i.e. DF_HIDDEN 0, // cooldown delay if used NULL, // function if used }, { MASK_TEST, "Test mask", "p_rebreather", U_MEMBER, &stat_mask_recon, 0, 0, NULL, }, { MASK_END, // dummy entry to mark the end of this group in the list "No mask", "i_fixme", U_END, &stat_blank, 0, 0, NULL, }, ... }
To be frank though I only know enough to be dangerous in C :s and self taught.
EDIT: Also aware of the amount of pollution with all those typedefs (and that is nothing compared to the complete code) but it is necessary with the engine.
Yep - I can follow it, though it's many been years since I worked in C/C++. But you see how your code could be incredibly dangerous once it was in a multi-developer environment? Or how hard it would be to maintain? Lets say you had a linked attribute to your health variable above. Maybe it's an associated representation like health_percentage_of_max; maybe it's an action that needs to be triggered when heatlh falls below a certain level like change_healthbar_colour(). Perhaps you want a special type of character whose health behaves differently - maybe they're undead and their health has two different tracks for holy damage and normal damage. I don't know - these are just examples. For the first, you need to make sure that ANY time the health attribute is changed, the health_percentage_of_max gets changed as well. If it's a class where health can only be changed by calling an appropriate method (such as decrementHealth() or setHealth()), you can wrap the change to health_percentage_of_max in the same function call. And due to encapsulation you know those methods can't be bypassed by another programmer (who might just be you in the future) saying "Oh, when this happens I'll need to reduce the health value by 100" and forgetting to update anything else. Or to take the second example where you want to trigger a specific action - again, you could add an event to the global queue when health falls below a certain value in the same way. That means anytime their health falls, that health bar on screen turns read, or they die or whatever. There are no exploits where someone forgot to do the value check and the character becomes immortal if they lose their last health point from a self-inflicted wound or a certain spell or whathaveyou. And in the third case, you can implement this by making some special sub-class of the Creature class called Undead that inherits all the normal functionality but overrides the "health" methods to get its special functionality. That way there's both no code duplication and no weird pointer to a method that gets orphaned when someone decides they need to add, remove or change a method on the more generic creature.
Just to illustrate. OO has real value which is why modern (non-Functional) languages, including C++, all follow it.
Gah - I'm done! This isn't meant to read as a rant. It's an interesting subject and I enjoy discussing it.
Believe me a lot of what you said has gone through my mind 100x while working on this - though I don't envision anyone else working on it atleast at any stage before it is fully rounded out.
I was initially self taught years ago (in the 1980's). I have programmed professionally in several roles since, although not for at least ten years now. I learned a huge amount from working with others. One of the lessons was to always use standard approaches. What I mean by that is to avoid 'clever' code wherever possible. Always try to write simple code even if it means a few more lines of typing. Whenever I've written something clever I can't make head nor tail of it a few months later.
Lets say you had a linked attribute to your health variable above. Maybe it's an associated representation like health_percentage_of_max; maybe it's an action that needs to be triggered when heatlh falls below a certain level like change_healthbar_colour(). Perhaps you want a special type of character whose health behaves differently - maybe they're undead and their health has two different tracks for holy damage and normal damage. I don't know - these are just examples. For the first, you need to make sure that ANY time the health attribute is changed, the health_percentage_of_max gets changed as well. If it's a class where health can only be changed by calling an appropriate method (such as decrementHealth() or setHealth()), you can wrap the change to health_percentage_of_max in the same function call. And due to encapsulation you know those methods can't be bypassed by another programmer (who might just be you in the future) saying "Oh, when this happens I'll need to reduce the health value by 100" and forgetting to update anything else.
void calcUpgrades(edict_t *ent)
{
int i;
upgrades_t *up;
int bonus;
ent->client->pers.stat.resist = 0;
ent->client->pers.stat.health = 0;
ent->client->pers.stat.regen = 0;
ent->client->pers.stat.hok = 0;
ent->client->pers.stat.hod = 0;
ent->client->pers.stat.dmg = 0;
ent->client->pers.stat.mg = 0;
ent->client->pers.stat.sg = 0;
ent->client->pers.stat.ss = 0;
ent->client->pers.stat.gren = 0;
ent->client->pers.stat.gl = 0;
ent->client->pers.stat.rl = 0;
ent->client->pers.stat.cg = 0;
ent->client->pers.stat.hb = 0;
ent->client->pers.stat.rg = 0;
ent->client->pers.stat.tr = 0;
ent->client->pers.stat.air = 0;
ent->client->pers.stat.jump = 0;
ent->client->pers.stat.tech = 0;
ent->client->pers.stat.unused1 = 0;
ent->client->pers.stat.unused2 = 0;
ent->client->pers.stat.unused3 = 0;
for (i = 0; i < AT_MAX; i++)
{
up = upgradeslist;
up += ent->client->pers.upgrades[i];
ent->client->pers.stat.resist += up->stat->resist;
ent->client->pers.stat.health += up->stat->health;
ent->client->pers.stat.regen += up->stat->regen;
ent->client->pers.stat.hok += up->stat->hok;
ent->client->pers.stat.hod += up->stat->hod;
ent->client->pers.stat.dmg += up->stat->dmg;
ent->client->pers.stat.mg += up->stat->mg;
ent->client->pers.stat.sg += up->stat->sg;
ent->client->pers.stat.ss += up->stat->ss;
ent->client->pers.stat.gren += up->stat->gren;
ent->client->pers.stat.gl += up->stat->gl;
ent->client->pers.stat.rl += up->stat->rl;
ent->client->pers.stat.cg += up->stat->cg;
ent->client->pers.stat.hb += up->stat->hb;
ent->client->pers.stat.rg += up->stat->rg;
ent->client->pers.stat.tr += up->stat->tr;
ent->client->pers.stat.air += up->stat->air;
ent->client->pers.stat.jump += up->stat->jump;
ent->client->pers.stat.tech += up->stat->tech;
ent->client->pers.stat.unused1 += up->stat->unused1;
ent->client->pers.stat.unused2 += up->stat->unused2;
ent->client->pers.stat.unused3 += up->stat->unused3;
}
bonus = 100 * ent->client->pers.stat.health;
ent->client->pers.max_health = ent->max_health = 100 + bonus;
}
Lots of good info here, will take a looks at some of the suggestions. Any of you have any programs you have made that you could share?
Looks like a few of you have lots of experience.
Private Sub StartService(ServiceName As String)
Try
Dim t = New TimeSpan(0, 0, 8)
Using s = New ServiceController(ServiceName)
If s IsNot Nothing Then
s.Start()
s.WaitForStatus(ServiceControllerStatus.Running, t)
End If
End Using
Catch ex As Exception
MsgBox("Error: " + ex.Message)
End Try
End Sub
Private Sub StopService(ServiceName As String)
Try
Dim t = New TimeSpan(0, 0, 8)
Using s = New ServiceController(ServiceName)
If s IsNot Nothing Then
s.Stop()
s.WaitForStatus(ServiceControllerStatus.Stopped, t)
End If
End Using
Catch ex As Exception
MsgBox("Error: " + ex.Message)
End Try
End Sub
Private Function GetServiceStatus(ServiceName As String) As String
Try
Using s = New ServiceController(ServiceName)
If s IsNot Nothing Then
Return s.Status().ToString()
End If
End Using
Catch ex As Exception
'MsgBox("Error: " + ex.Message)
End Try
Return "Service Not Found."
End Function